移动部分函数到消息处理器, 保持链接过滤器的纯粹

This commit is contained in:
wood chen 2024-09-20 00:42:57 +08:00
parent c5acd7cd7c
commit 6dc34b3aaf
6 changed files with 147 additions and 1763 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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"]

View File

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

View File

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

View File

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