129 lines
2.9 KiB
Go
129 lines
2.9 KiB
Go
|
|
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
|
||
|
|
}
|