mirror of
https://github.com/woodchen-ink/Q58Bot.git
synced 2025-07-18 05:42:06 +08:00
移动部分函数到消息处理器, 保持链接过滤器的纯粹
This commit is contained in:
parent
c5acd7cd7c
commit
6dc34b3aaf
@ -1,4 +0,0 @@
|
||||
BOT_TOKEN=your_bot_token
|
||||
ADMIN_ID=5912366993
|
||||
CHAT_ID=your_chat_id
|
||||
SYMBOLS=BTC/USDT,ETH/USDT
|
1612
.gitignore
vendored
1612
.gitignore
vendored
File diff suppressed because it is too large
Load Diff
43
Dockerfile
43
Dockerfile
@ -1,43 +0,0 @@
|
||||
# 使用官方 Go 镜像作为构建环境
|
||||
FROM golang:1.22 AS builder
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制 go mod 和 sum 文件
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
# 下载依赖
|
||||
RUN go mod download
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 编译应用
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
|
||||
|
||||
# 使用轻量级的 alpine 镜像作为运行环境
|
||||
FROM alpine:latest
|
||||
|
||||
# 安装 ca-certificates
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# 从构建阶段复制编译好的应用
|
||||
COPY --from=builder /app/main .
|
||||
|
||||
# 设置环境变量
|
||||
ENV BOT_TOKEN=""
|
||||
ENV ADMIN_ID=""
|
||||
ENV SYMBOLS=""
|
||||
ENV DEBUG_MODE="false"
|
||||
|
||||
# 创建数据目录
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# 暴露端口(如果你的应用需要的话)
|
||||
# EXPOSE 8080
|
||||
|
||||
# 运行应用
|
||||
CMD ["./main"]
|
52
README.md
52
README.md
@ -57,10 +57,60 @@ docker-compose up -d
|
||||
- 确保 Telegram 机器人已被添加到目标群组,并被赋予管理员权限
|
||||
- 定期检查日志以确保服务正常运行
|
||||
|
||||
## 项目结构
|
||||
```
|
||||
Q58Bot/
|
||||
│
|
||||
├── core/
|
||||
│ ├── bot_commands.go # 注册机器人命令
|
||||
│ ├── database.go # 数据库操作
|
||||
│ ├── init.go # 初始化全局变量
|
||||
│ └── ratelimiter.go # 限速器
|
||||
│
|
||||
├── service/
|
||||
│ ├── binance/
|
||||
│ │ └── binance.go # 获取币安价格信息
|
||||
│ │
|
||||
| |── group_member_management/
|
||||
| | └── group_member_management.go # 对群组进行管理
|
||||
│ │
|
||||
│ ├── link_filter/
|
||||
│ │ └── link_filter.go # 链接过滤器
|
||||
│ │
|
||||
│ ├── prompt_reply/
|
||||
│ | └── prompt_reply.go # 提示词自动回复
|
||||
│ │
|
||||
│ └── message_handler.go # 消息处理器
|
||||
│
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile.multi
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── main.go # 入口文件
|
||||
└── README.md
|
||||
```
|
||||
## 设计规范
|
||||
|
||||
> 自己记录
|
||||
|
||||
1. 关于数据库的操作, 需要在运行时进行一次加载数据
|
||||
2. 任何操作需要添加相关日志, 日志需要包含时间戳
|
||||
3. 要使用全局的数据库实例`core.DB`, 相关代码如下:
|
||||
- `init.go`里
|
||||
``` go
|
||||
// 初始化数据库
|
||||
DB_FILE = filepath.Join("/app/data", "q58.db")
|
||||
var err error
|
||||
DB, err = NewDatabase()
|
||||
if err != nil {
|
||||
return fmt.Errorf("初始化数据库失败: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎提交 Issues 和 Pull Requests 来帮助改进这个项目。
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT License](LICENSE)
|
||||
[MIT License](LICENSE)
|
@ -14,40 +14,40 @@ import (
|
||||
var logger = log.New(log.Writer(), "LinkFilter: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
type LinkFilter struct {
|
||||
keywords []string
|
||||
whitelist []string
|
||||
linkPattern *regexp.Regexp
|
||||
mu sync.RWMutex
|
||||
Keywords []string
|
||||
Whitelist []string
|
||||
LinkPattern *regexp.Regexp
|
||||
Mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewLinkFilter() (*LinkFilter, error) {
|
||||
lf := &LinkFilter{
|
||||
linkPattern: regexp.MustCompile(`(?i)\b(?:(?:https?://)?(?:(?:www\.)?(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(?:t\.me|telegram\.me))(?:/[^\s]*)?)`),
|
||||
LinkPattern: regexp.MustCompile(`(?i)\b(?:(?:https?://)?(?:(?:www\.)?(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(?:t\.me|telegram\.me))(?:/[^\s]*)?)`),
|
||||
}
|
||||
|
||||
if err := lf.LoadDataFromFile(); err != nil {
|
||||
if err := lf.LoadDataFromDatabase(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lf, nil
|
||||
}
|
||||
|
||||
func (lf *LinkFilter) LoadDataFromFile() error {
|
||||
lf.mu.Lock()
|
||||
defer lf.mu.Unlock()
|
||||
func (lf *LinkFilter) LoadDataFromDatabase() error {
|
||||
lf.Mu.Lock()
|
||||
defer lf.Mu.Unlock()
|
||||
|
||||
var err error
|
||||
lf.keywords, err = core.DB.GetAllKeywords()
|
||||
lf.Keywords, err = core.DB.GetAllKeywords()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lf.whitelist, err = core.DB.GetAllWhitelist()
|
||||
lf.Whitelist, err = core.DB.GetAllWhitelist()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Printf("Loaded %d keywords and %d whitelist entries from database", len(lf.keywords), len(lf.whitelist))
|
||||
logger.Printf("Loaded %d Keywords and %d Whitelist entries from database", len(lf.Keywords), len(lf.Whitelist))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ func (lf *LinkFilter) domainMatch(domain, whiteDomain string) bool {
|
||||
}
|
||||
func (lf *LinkFilter) IsWhitelisted(link string) bool {
|
||||
domain := lf.ExtractDomain(link)
|
||||
for _, whiteDomain := range lf.whitelist {
|
||||
for _, whiteDomain := range lf.Whitelist {
|
||||
if lf.domainMatch(domain, whiteDomain) {
|
||||
logger.Printf("Whitelist check for %s: Passed (matched %s)", link, whiteDomain)
|
||||
return true
|
||||
@ -115,92 +115,3 @@ func (lf *LinkFilter) IsWhitelisted(link string) bool {
|
||||
logger.Printf("Whitelist check for %s: Failed", link)
|
||||
return false
|
||||
}
|
||||
|
||||
func (lf *LinkFilter) AddKeyword(keyword string) error {
|
||||
if lf.linkPattern.MatchString(keyword) {
|
||||
keyword = lf.NormalizeLink(keyword)
|
||||
}
|
||||
keyword = strings.TrimPrefix(keyword, "/")
|
||||
for _, k := range lf.keywords {
|
||||
if k == keyword {
|
||||
logger.Printf("Keyword already exists: %s", keyword)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := core.DB.AddKeyword(keyword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Printf("New keyword added: %s", keyword)
|
||||
return lf.LoadDataFromFile()
|
||||
}
|
||||
|
||||
func (lf *LinkFilter) RemoveKeyword(keyword string) bool {
|
||||
removed, err := core.DB.RemoveKeyword(keyword)
|
||||
if err != nil {
|
||||
logger.Printf("Error removing keyword: %v", err)
|
||||
return false
|
||||
}
|
||||
if removed {
|
||||
lf.LoadDataFromFile()
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func (lf *LinkFilter) RemoveKeywordsContaining(substring string) ([]string, error) {
|
||||
removed, err := core.DB.RemoveKeywordsContaining(substring)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = lf.LoadDataFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return removed, nil
|
||||
}
|
||||
|
||||
// 检查消息是否包含关键词或者非白名单链接
|
||||
func (lf *LinkFilter) ShouldFilter(text string) (bool, []string) {
|
||||
logger.Printf("Checking text: %s", text)
|
||||
|
||||
lf.mu.RLock()
|
||||
defer lf.mu.RUnlock()
|
||||
|
||||
for _, keyword := range lf.keywords {
|
||||
if strings.Contains(strings.ToLower(text), strings.ToLower(keyword)) {
|
||||
logger.Printf("文字包含关键字: %s", keyword)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
links := lf.linkPattern.FindAllString(text, -1)
|
||||
logger.Printf("找到链接: %v", links)
|
||||
|
||||
var newNonWhitelistedLinks []string
|
||||
for _, link := range links {
|
||||
normalizedLink := lf.NormalizeLink(link)
|
||||
if !lf.IsWhitelisted(normalizedLink) {
|
||||
logger.Printf("链接未列入白名单: %s", normalizedLink)
|
||||
if !lf.containsKeyword(normalizedLink) {
|
||||
newNonWhitelistedLinks = append(newNonWhitelistedLinks, normalizedLink)
|
||||
lf.AddKeyword(normalizedLink) // 注意:这里会修改 lf.keywords,可能需要额外的锁
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newNonWhitelistedLinks) > 0 {
|
||||
logger.Printf("发现新的非白名单链接: %v", newNonWhitelistedLinks)
|
||||
}
|
||||
return false, newNonWhitelistedLinks
|
||||
}
|
||||
|
||||
func (lf *LinkFilter) containsKeyword(link string) bool {
|
||||
for _, keyword := range lf.keywords {
|
||||
if keyword == link {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ import (
|
||||
"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) {
|
||||
// 检查更新是否包含消息,如果不包含则直接返回。
|
||||
@ -64,7 +68,7 @@ func processMessage(bot *tgbotapi.BotAPI, message *tgbotapi.Message, linkFilter
|
||||
// 如果不是管理员,才进行链接过滤
|
||||
if !core.IsAdmin(message.From.ID) {
|
||||
// 判断消息是否应当被过滤及找出新的非白名单链接
|
||||
shouldFilter, newLinks := linkFilter.ShouldFilter(message.Text)
|
||||
shouldFilter, newLinks := ShouldFilter(message.Text, linkFilter)
|
||||
if shouldFilter {
|
||||
// 记录被过滤的消息
|
||||
log.Printf("消息应该被过滤: %s", message.Text)
|
||||
@ -430,3 +434,81 @@ func handleDeleteWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, doma
|
||||
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user