58 lines
2.5 KiB
Go
58 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Flood/spam detection minimum version (roadmap.md §2.7-C, PRD.md §4
|
|
// 엣지케이스: "상대가 짧은 시간에 메시지 도배 -> 스팸 감지 임계치 초과 시 응대
|
|
// 중단, 사용자에게 보고 (P1이지만 안전 관련이라 v1 최소 버전 필요)").
|
|
//
|
|
// These two constants are a conservative, clearly-labeled v1-minimum
|
|
// placeholder, NOT a PoC-validated number -- unlike the autonomy-default/
|
|
// whitelist-default questions in roadmap.md §3 ("PoC 결과가 있어야 정할 수
|
|
// 있는 것"), this is a technical safety minimum that has to exist for P0
|
|
// coverage, so it's fine to ship a placeholder and revisit with real usage
|
|
// data later rather than leaving the gate unbuilt.
|
|
const (
|
|
// floodMessageThreshold is how many incoming messages from the
|
|
// counterpart within floodWindow count as "도배".
|
|
floodMessageThreshold = 5
|
|
// floodWindow is the trailing window floodMessageThreshold is counted over.
|
|
floodWindow = 2 * time.Minute
|
|
)
|
|
|
|
// floodIncomingCount counts messages in conversationID sent by anyone other
|
|
// than ownerUserID (i.e. the counterpart's own words, not the twin's
|
|
// auto-sends nor the owner's own human-typed messages, both of which use
|
|
// SenderID == ownerUserID -- see sendMessageRequest.SenderID) within the
|
|
// trailing floodWindow. Retracted messages still count: retraction undoes an
|
|
// auto-send the twin/owner made, it says nothing about whether the peer was
|
|
// flooding.
|
|
func floodIncomingCount(db *gorm.DB, conversationID, ownerUserID uint) int64 {
|
|
var count int64
|
|
db.Model(&Message{}).
|
|
Where("conversation_id = ? AND sender_id != ? AND created_at >= ?",
|
|
conversationID, ownerUserID, time.Now().Add(-floodWindow)).
|
|
Count(&count)
|
|
return count
|
|
}
|
|
|
|
// floodDetected reports whether the counterpart has crossed
|
|
// floodMessageThreshold within floodWindow for this conversation, plus the
|
|
// count for the resulting EscalationLog reason string.
|
|
func floodDetected(db *gorm.DB, conversationID, ownerUserID uint) (bool, int64) {
|
|
count := floodIncomingCount(db, conversationID, ownerUserID)
|
|
return count > floodMessageThreshold, count
|
|
}
|
|
|
|
// floodReason formats the Korean EscalationLog copy for a flood auto-pause,
|
|
// matching the tone of the existing escalation_filter reasons.
|
|
func floodReason(count int64) string {
|
|
return fmt.Sprintf("도배 감지: 최근 %d분 동안 상대로부터 메시지 %d건 수신 (임계치 %d건) -- 자동응대 일시중단",
|
|
int(floodWindow/time.Minute), count, floodMessageThreshold)
|
|
}
|