// Copyright 2026 tamakyi. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package question import ( stdctx "context" "sync" "time" "github.com/sirupsen/logrus" "github.com/tamakyi/TamaBox/internal/conf" "github.com/tamakyi/TamaBox/internal/db" "github.com/tamakyi/TamaBox/internal/mail" "github.com/tamakyi/TamaBox/internal/shared" ) // 举报阈值:达到任一条件就发一封汇总邮件给运维。 // // 为什么是 8 / 20 而不是更低:一次粉圈摩擦里,围观者顺手举报同一内容很容易攒到三五条, // 阈值太低会天天误报,最后这个告警会被无视;而组织化挂人往往短时间内几十条。 const ( abuseAlertThreshold24h = 8 abuseAlertThreshold7d = 20 abuseAlertCooldown = 24 * time.Hour ) // abuseAlertCooldowns 每个箱主上次发送阈值告警的时间。 // // 用进程内 map 而不是加表:告警是「一次性提醒」,重启后丢失最坏结果是多发一封, // 而为此新增一张表和一套清理逻辑不划算。并发下用互斥锁保证同一箱主不会连发。 var ( abuseAlertMu sync.Mutex abuseAlertCooldowns = make(map[uint]time.Time) ) func allowAbuseAlert(userID uint) bool { abuseAlertMu.Lock() defer abuseAlertMu.Unlock() if last, ok := abuseAlertCooldowns[userID]; ok && time.Since(last) < abuseAlertCooldown { return false } abuseAlertCooldowns[userID] = time.Now() return true } // notifyAbuseIfNeeded 在举报写入后判断是否达到告警阈值,异步发送汇总邮件。 // // 只发「该看一眼」的信号,不做任何自动处置:举报数达标不等于内容违规, // 尤其这类互殴场景,自动处置会直接变成站队。 func notifyAbuseIfNeeded(pageUser *db.User) { if conf.Security.SecurityAlertEmail == "" { return } shared.SafeGo(func() { ctx := stdctx.Background() count24, err := db.Reports.CountByUserSince(ctx, pageUser.ID, time.Now().Add(-24*time.Hour)) if err != nil { logrus.WithError(err).Error("Failed to count reports within 24h") return } count7d, err := db.Reports.CountByUserSince(ctx, pageUser.ID, time.Now().AddDate(0, 0, -7)) if err != nil { logrus.WithError(err).Error("Failed to count reports within 7d") return } if count24 < abuseAlertThreshold24h && count7d < abuseAlertThreshold7d { return } if !allowAbuseAlert(pageUser.ID) { return } if err := mail.SendAbuseAlertMail(pageUser.Domain, pageUser.Name, count24, count7d); err != nil { logrus.WithError(err).Error("Failed to send abuse alert mail") return } logrus.WithFields(logrus.Fields{ "user_id": pageUser.ID, "domain": pageUser.Domain, "count24": count24, "count7d": count7d, }).Warn("Abuse alert mail sent") }) }