75 lines
1.5 KiB
Go
75 lines
1.5 KiB
Go
package viral
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"haixun-backend/internal/library/placement"
|
|
)
|
|
|
|
const (
|
|
MinEngagementScore = 24
|
|
MinLikeCount = 8
|
|
HotEngagementScore = 80
|
|
)
|
|
|
|
// ScorePost ranks viral imitation candidates by engagement signals.
|
|
func ScorePost(likes, replies int) int {
|
|
if likes < 0 {
|
|
likes = 0
|
|
}
|
|
if replies < 0 {
|
|
replies = 0
|
|
}
|
|
score := likes*2 + replies*3
|
|
if likes >= 20 && replies >= 3 {
|
|
score += 15
|
|
}
|
|
if replies > 0 && likes > 0 && replies*3 >= likes {
|
|
score += 10
|
|
}
|
|
if score > 100 {
|
|
return 100
|
|
}
|
|
return score
|
|
}
|
|
|
|
func PriorityLabel(score int) string {
|
|
if score >= HotEngagementScore {
|
|
return "hot"
|
|
}
|
|
if score >= MinEngagementScore {
|
|
return "warm"
|
|
}
|
|
return "cold"
|
|
}
|
|
|
|
func PassesViralCandidate(text string, likes, replies, engagementScore int, exclusions []string) bool {
|
|
if strings.TrimSpace(text) == "" {
|
|
return false
|
|
}
|
|
if placement.MatchesExclusion(text, exclusions) {
|
|
return false
|
|
}
|
|
if placement.LooksLikeCasualChat(text) && !hasViralSignal(text) {
|
|
return false
|
|
}
|
|
if likes < MinLikeCount && engagementScore < MinEngagementScore {
|
|
return false
|
|
}
|
|
if engagementScore < MinEngagementScore {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func hasViralSignal(text string) bool {
|
|
text = strings.ToLower(text)
|
|
signals := []string{"分享", "推薦", "心得", "技巧", "方法", "語錄", "故事", "怎麼", "為什麼", "必看"}
|
|
for _, s := range signals {
|
|
if strings.Contains(text, s) {
|
|
return true
|
|
}
|
|
}
|
|
return len([]rune(text)) >= 40
|
|
}
|