mirror of
https://github.com/woodchen-ink/Q58Bot.git
synced 2025-07-18 13:52:07 +08:00
515 lines
15 KiB
Go
515 lines
15 KiB
Go
// 消息处理函数
|
||
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"strings"
|
||
"time"
|
||
|
||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||
"github.com/woodchen-ink/Q58Bot/core"
|
||
"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"
|
||
)
|
||
|
||
var (
|
||
logger = log.New(log.Writer(), "MessageHandler: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||
)
|
||
|
||
// handleUpdate 处理所有传入的更新信息,包括消息和命令, 然后分开处理。
|
||
func handleUpdate(bot *tgbotapi.BotAPI, update tgbotapi.Update, linkFilter *link_filter.LinkFilter, rateLimiter *core.RateLimiter) {
|
||
// 检查更新是否包含消息,如果不包含则直接返回。
|
||
if update.Message == nil {
|
||
return
|
||
}
|
||
|
||
// 如果消息来自私聊且发送者是预定义的管理员,调用处理管理员命令的函数。
|
||
if update.Message.Chat.Type == "private" && update.Message.From.ID == core.ADMIN_ID {
|
||
handleAdminCommand(bot, update.Message)
|
||
return
|
||
}
|
||
|
||
// 如果消息来自群聊且通过了速率限制器的检查,调用处理普通消息的函数。
|
||
if update.Message.Chat.Type != "private" && rateLimiter.Allow() {
|
||
processMessage(bot, update.Message, linkFilter)
|
||
}
|
||
}
|
||
|
||
// 处理管理员私聊消息
|
||
func handleAdminCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
|
||
command := message.Command()
|
||
args := message.CommandArguments()
|
||
|
||
switch command {
|
||
case "add", "delete", "list", "deletecontaining":
|
||
HandleKeywordCommand(bot, message, command, args)
|
||
case "addwhite", "delwhite", "listwhite":
|
||
HandleWhitelistCommand(bot, message, command, args)
|
||
case "prompt":
|
||
prompt_reply.HandlePromptCommand(bot, message)
|
||
default:
|
||
bot.Send(tgbotapi.NewMessage(message.Chat.ID, "未知命令, 听不懂"))
|
||
}
|
||
}
|
||
|
||
// processMessage 处理群里接收到的消息。
|
||
func processMessage(bot *tgbotapi.BotAPI, message *tgbotapi.Message, linkFilter *link_filter.LinkFilter) {
|
||
// 记录消息内容
|
||
log.Printf("Processing message: %s", message.Text)
|
||
|
||
// 处理 /ban 命令
|
||
if message.ReplyToMessage != nil && message.Text == "/ban" {
|
||
group_member_management.HandleBanCommand(bot, message)
|
||
return
|
||
}
|
||
|
||
// 如果不是管理员,才进行链接过滤
|
||
if !core.IsAdmin(message.From.ID) {
|
||
// 判断消息是否应当被过滤及找出新的非白名单链接
|
||
shouldFilter, newLinks := ShouldFilter(message.Text, linkFilter)
|
||
if shouldFilter {
|
||
// 记录被过滤的消息
|
||
log.Printf("消息应该被过滤: %s", message.Text)
|
||
// 删除原始消息
|
||
deleteMsg := tgbotapi.NewDeleteMessage(message.Chat.ID, message.MessageID)
|
||
_, err := bot.Request(deleteMsg)
|
||
if err != nil {
|
||
// 删除消息失败时记录错误
|
||
log.Printf("删除消息失败: %v", err)
|
||
}
|
||
|
||
// 发送提示消息
|
||
notification := tgbotapi.NewMessage(message.Chat.ID, "已撤回该消息。注:一个链接不能发两次.")
|
||
sent, err := bot.Send(notification)
|
||
if err != nil {
|
||
// 发送通知失败时记录错误
|
||
log.Printf("发送通知失败: %v", err)
|
||
} else {
|
||
// 3分钟后删除提示消息
|
||
go deleteMessageAfterDelay(bot, message.Chat.ID, sent.MessageID, 3*time.Minute)
|
||
}
|
||
// 结束处理
|
||
return
|
||
}
|
||
// 如果发现新的非白名单链接
|
||
if len(newLinks) > 0 {
|
||
// 记录新的非白名单链接
|
||
log.Printf("发现新的非白名单链接: %v", newLinks)
|
||
}
|
||
}
|
||
|
||
// 检查消息文本是否匹配预设的提示词并回复
|
||
if reply, found := prompt_reply.GetPromptReply(message.Text); found {
|
||
// 创建回复消息
|
||
replyMsg := tgbotapi.NewMessage(message.Chat.ID, reply)
|
||
replyMsg.ReplyToMessageID = message.MessageID
|
||
// sent, err := bot.Send(replyMsg)
|
||
// if err != nil {
|
||
// 发送回复失败时记录错误
|
||
// log.Printf("未能发送及时回复: %v", err)
|
||
// } else {
|
||
// // 3分钟后删除回复消息
|
||
// go deleteMessageAfterDelay(bot, message.Chat.ID, sent.MessageID, 3*time.Minute)
|
||
// }
|
||
}
|
||
}
|
||
|
||
func RunMessageHandler() error {
|
||
log.Println("消息处理器启动...")
|
||
|
||
// 加载提示回复数据
|
||
err := prompt_reply.Manager.LoadDataFromDatabase()
|
||
if err != nil {
|
||
log.Printf("加载提示回复数据失败: %v", err)
|
||
// 考虑是否要因为这个错误停止启动
|
||
// return fmt.Errorf("加载提示回复数据失败: %w", err)
|
||
}
|
||
|
||
baseDelay := time.Second
|
||
maxDelay := 5 * time.Minute
|
||
delay := baseDelay
|
||
|
||
for {
|
||
err := func() error {
|
||
log.Printf("Attempting to create bot with token: %s", core.BOT_TOKEN)
|
||
bot, err := tgbotapi.NewBotAPI(core.BOT_TOKEN)
|
||
if err != nil {
|
||
log.Printf("Error details: %+v", err)
|
||
return fmt.Errorf("failed to create bot: %w", err)
|
||
}
|
||
|
||
bot.Debug = core.DEBUG_MODE
|
||
|
||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
||
|
||
err = core.RegisterCommands(bot)
|
||
if err != nil {
|
||
return fmt.Errorf("error registering commands: %w", err)
|
||
}
|
||
|
||
linkFilter, err := link_filter.NewLinkFilter()
|
||
if err != nil {
|
||
return fmt.Errorf("failed to create LinkFilter: %v", err)
|
||
}
|
||
|
||
rateLimiter := core.NewRateLimiter()
|
||
|
||
u := tgbotapi.NewUpdate(0)
|
||
u.Timeout = 60
|
||
|
||
updates := bot.GetUpdatesChan(u)
|
||
|
||
for update := range updates {
|
||
go handleUpdate(bot, update, linkFilter, rateLimiter)
|
||
}
|
||
|
||
return nil
|
||
}()
|
||
|
||
if err != nil {
|
||
log.Printf("Bot encountered an error: %v", err)
|
||
log.Printf("Attempting to restart in %v...", delay)
|
||
time.Sleep(delay)
|
||
|
||
delay *= 2
|
||
if delay > maxDelay {
|
||
delay = maxDelay
|
||
}
|
||
} else {
|
||
delay = baseDelay
|
||
log.Println("Bot disconnected. Attempting to restart immediately...")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 下面是辅助函数部分
|
||
//
|
||
//
|
||
//
|
||
|
||
const (
|
||
maxMessageLength = 4000
|
||
)
|
||
|
||
func deleteMessageAfterDelay(bot *tgbotapi.BotAPI, chatID int64, messageID int, delay time.Duration) {
|
||
go func() {
|
||
time.Sleep(delay)
|
||
deleteMsg := tgbotapi.NewDeleteMessage(chatID, messageID)
|
||
_, err := bot.Request(deleteMsg)
|
||
if err != nil {
|
||
log.Printf("删除消息失败 (ChatID: %d, MessageID: %d): %v", chatID, messageID, err)
|
||
}
|
||
}()
|
||
}
|
||
|
||
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)
|
||
|
||
switch command {
|
||
case "list":
|
||
handleListKeywords(bot, message)
|
||
case "add":
|
||
handleAddKeyword(bot, message, args)
|
||
case "delete":
|
||
handleDeleteKeyword(bot, message, args)
|
||
case "deletecontaining":
|
||
handleDeleteContainingKeyword(bot, message, args)
|
||
default:
|
||
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, "获取关键词列表时发生错误。")
|
||
return
|
||
}
|
||
if len(keywords) == 0 {
|
||
sendMessage(bot, message.Chat.ID, "关键词列表为空。")
|
||
} else {
|
||
SendLongMessage(bot, message.Chat.ID, "当前关键词列表:", keywords)
|
||
}
|
||
}
|
||
|
||
func handleAddKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
|
||
if keyword == "" {
|
||
sendErrorMessage(bot, message.Chat.ID, "请提供要添加的关键词。")
|
||
return
|
||
}
|
||
|
||
exists, err := core.DB.KeywordExists(keyword)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, "检查关键词时发生错误。")
|
||
return
|
||
}
|
||
if !exists {
|
||
err = core.DB.AddKeyword(keyword)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, "添加关键词时发生错误。")
|
||
} else {
|
||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已添加。", keyword))
|
||
}
|
||
} else {
|
||
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, "请提供要删除的关键词。")
|
||
return
|
||
}
|
||
|
||
removed, err := core.DB.RemoveKeyword(keyword)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("删除关键词 '%s' 时发生错误: %v", keyword, err))
|
||
return
|
||
}
|
||
|
||
if removed {
|
||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已成功删除。", keyword))
|
||
} else {
|
||
handleSimilarKeywords(bot, message, keyword)
|
||
}
|
||
}
|
||
|
||
func handleSimilarKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
|
||
similarKeywords, err := core.DB.SearchKeywords(keyword)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, "搜索关键词时发生错误。")
|
||
return
|
||
}
|
||
if len(similarKeywords) > 0 {
|
||
SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("未能删除关键词 '%s'。\n\n以下是相似的关键词:", keyword), similarKeywords)
|
||
} else {
|
||
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, "请提供要删除的子字符串。")
|
||
return
|
||
}
|
||
|
||
removedKeywords, err := core.DB.RemoveKeywordsContaining(substring)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, "删除关键词时发生错误。")
|
||
return
|
||
}
|
||
if len(removedKeywords) > 0 {
|
||
SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("已删除包含 '%s' 的以下关键词:", substring), removedKeywords)
|
||
} else {
|
||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("没有找到包含 '%s' 的关键词。", substring))
|
||
}
|
||
}
|
||
|
||
func HandleWhitelistCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, command string, args string) {
|
||
args = strings.TrimSpace(args)
|
||
|
||
switch command {
|
||
case "listwhite":
|
||
handleListWhitelist(bot, message)
|
||
case "addwhite":
|
||
handleAddWhitelist(bot, message, args)
|
||
case "delwhite":
|
||
handleDeleteWhitelist(bot, message, args)
|
||
default:
|
||
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))
|
||
return
|
||
}
|
||
if len(whitelist) == 0 {
|
||
sendMessage(bot, message.Chat.ID, "白名单为空。")
|
||
} else {
|
||
SendLongMessage(bot, message.Chat.ID, "白名单域名列表:", whitelist)
|
||
}
|
||
}
|
||
|
||
func handleAddWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) {
|
||
if domain == "" {
|
||
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))
|
||
return
|
||
}
|
||
if exists {
|
||
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))
|
||
return
|
||
}
|
||
|
||
exists, err = core.DB.WhitelistExists(domain)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证添加操作时发生错误: %v", err))
|
||
return
|
||
}
|
||
if exists {
|
||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功添加到白名单。", domain))
|
||
} else {
|
||
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, "请提供要删除的域名。")
|
||
return
|
||
}
|
||
|
||
domain = strings.ToLower(domain)
|
||
exists, err := core.DB.WhitelistExists(domain)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
|
||
return
|
||
}
|
||
if !exists {
|
||
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))
|
||
return
|
||
}
|
||
|
||
exists, err = core.DB.WhitelistExists(domain)
|
||
if err != nil {
|
||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证删除操作时发生错误: %v", err))
|
||
return
|
||
}
|
||
if !exists {
|
||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功从白名单中删除。", domain))
|
||
} else {
|
||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能从白名单中删除域名 '%s'。", domain))
|
||
}
|
||
}
|
||
|
||
func addNewKeyword(keyword string) error {
|
||
exists, err := core.DB.KeywordExists(keyword)
|
||
if err != nil {
|
||
return fmt.Errorf("检查关键词时发生错误: %v", err)
|
||
}
|
||
if !exists {
|
||
err = core.DB.AddKeyword(keyword)
|
||
if err != nil {
|
||
return fmt.Errorf("添加关键词时发生错误: %v", err)
|
||
}
|
||
logger.Printf("新关键词已添加: %s", keyword)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ShouldFilter 检查消息是否包含关键词或者非白名单链接
|
||
func ShouldFilter(text string, linkFilter *link_filter.LinkFilter) (bool, []string) {
|
||
logger.Printf("Checking text: %s", text)
|
||
|
||
if containsKeyword(text, linkFilter) {
|
||
return true, nil
|
||
}
|
||
|
||
links := extractLinks(text, linkFilter)
|
||
return processLinks(links, linkFilter)
|
||
}
|
||
|
||
func containsKeyword(text string, linkFilter *link_filter.LinkFilter) bool {
|
||
linkFilter.Mu.RLock()
|
||
defer linkFilter.Mu.RUnlock()
|
||
|
||
for _, keyword := range linkFilter.Keywords {
|
||
if strings.Contains(strings.ToLower(text), strings.ToLower(keyword)) {
|
||
logger.Printf("文字包含关键字: %s", keyword)
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func extractLinks(text string, linkFilter *link_filter.LinkFilter) []string {
|
||
linkFilter.Mu.RLock()
|
||
defer linkFilter.Mu.RUnlock()
|
||
|
||
links := linkFilter.LinkPattern.FindAllString(text, -1)
|
||
logger.Printf("找到链接: %v", links)
|
||
return links
|
||
}
|
||
|
||
func processLinks(links []string, linkFilter *link_filter.LinkFilter) (bool, []string) {
|
||
var newNonWhitelistedLinks []string
|
||
|
||
for _, link := range links {
|
||
normalizedLink := linkFilter.NormalizeLink(link)
|
||
if !linkFilter.IsWhitelisted(normalizedLink) {
|
||
logger.Printf("链接未列入白名单: %s", normalizedLink)
|
||
if !containsKeyword(normalizedLink, linkFilter) {
|
||
newNonWhitelistedLinks = append(newNonWhitelistedLinks, normalizedLink)
|
||
err := addNewKeyword(normalizedLink)
|
||
if err != nil {
|
||
logger.Printf("添加关键词时发生错误: %v", err)
|
||
}
|
||
// 如果成功添加了新关键词,更新 linkFilter 的 Keywords
|
||
linkFilter.Mu.Lock()
|
||
linkFilter.Keywords = append(linkFilter.Keywords, normalizedLink)
|
||
linkFilter.Mu.Unlock()
|
||
} else {
|
||
return true, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(newNonWhitelistedLinks) > 0 {
|
||
logger.Printf("发现新的非白名单链接: %v", newNonWhitelistedLinks)
|
||
}
|
||
return false, newNonWhitelistedLinks
|
||
}
|