thread-master/backend/internal/library/viral/enrich_accounts.go

129 lines
2.9 KiB
Go
Raw Permalink Normal View History

2026-06-26 08:37:04 +00:00
package viral
import (
"sort"
"strings"
"haixun-backend/internal/library/placement"
missionentity "haixun-backend/internal/model/copy_mission/domain/entity"
)
func EnrichSimilarAccounts(existing []missionentity.SimilarAccount, posts []placement.ScanCandidate, limit int) []missionentity.SimilarAccount {
if limit <= 0 {
limit = MaxSimilarAccounts
}
byUser := map[string]missionentity.SimilarAccount{}
order := []string{}
for _, item := range existing {
user := strings.ToLower(strings.TrimSpace(item.Username))
if user == "" {
continue
}
if _, ok := byUser[user]; !ok {
order = append(order, user)
}
byUser[user] = item
}
type authorScore struct {
username string
score int
text string
}
authors := map[string]authorScore{}
for _, post := range posts {
user := strings.TrimSpace(post.Author)
if user == "" || !isValidUsername(user) {
continue
}
key := strings.ToLower(user)
prev := authors[key]
prev.username = user
prev.score += post.EngagementScore
if prev.text == "" && strings.TrimSpace(post.Text) != "" {
prev.text = strings.TrimSpace(post.Text)
}
authors[key] = prev
}
ranked := make([]authorScore, 0, len(authors))
for _, item := range authors {
ranked = append(ranked, item)
}
sort.Slice(ranked, func(i, j int) bool { return ranked[i].score > ranked[j].score })
for _, item := range ranked {
key := strings.ToLower(item.username)
if _, ok := byUser[key]; ok {
continue
}
reason := "本次海巡高互動作者"
if item.text != "" {
runes := []rune(item.text)
if len(runes) > 80 {
reason = string(runes[:80])
} else {
reason = item.text
}
}
conf := "medium"
if item.score >= 200 {
conf = "high"
}
byUser[key] = missionentity.SimilarAccount{
Username: item.username,
Reason: reason,
Source: "scan",
Confidence: conf,
ProfileURL: "https://www.threads.net/@" + item.username,
}
order = append(order, key)
}
out := make([]missionentity.SimilarAccount, 0, len(order))
for _, key := range order {
if acc, ok := byUser[key]; ok {
out = append(out, acc)
}
}
if len(out) > limit {
out = out[:limit]
}
return out
}
func AccountTagsFromSimilar(accounts []missionentity.SimilarAccount, max int) []SuggestedTag {
if max <= 0 {
max = 2
}
out := []SuggestedTag{}
for _, acc := range accounts {
user := strings.TrimSpace(acc.Username)
if user == "" {
continue
}
out = append(out, SuggestedTag{
Tag: "@" + user,
Reason: acc.Reason,
SearchType: "帳號",
})
if len(out) >= max {
break
}
}
return out
}
func ToEntitySimilarAccounts(items []SimilarAccount) []missionentity.SimilarAccount {
out := make([]missionentity.SimilarAccount, 0, len(items))
for _, item := range items {
out = append(out, missionentity.SimilarAccount{
Username: item.Username,
Reason: item.Reason,
Source: item.Source,
Confidence: item.Confidence,
ProfileURL: item.ProfileURL,
})
}
return out
}