package viral import ( "strings" missionentity "haixun-backend/internal/model/copy_mission/domain/entity" accountentity "haixun-backend/internal/model/threads_account/domain/entity" ) // ExcludedKnownAccountUsernames returns usernames marked excluded in cross-mission memory. func ExcludedKnownAccountUsernames(known map[string]accountentity.KnownAccountProfile) []string { if len(known) == 0 { return nil } out := make([]string, 0) seen := map[string]struct{}{} for key, item := range known { if item.Status != accountentity.KnownAccountStatusExcluded { continue } user := strings.TrimSpace(key) if user == "" { continue } lower := strings.ToLower(user) if _, ok := seen[lower]; ok { continue } seen[lower] = struct{}{} out = append(out, user) } return out } // ApplyKnownAccountMemory applies cross-mission memory to freshly merged similar // accounts: inherit excluded status and boost confidence for repeat sightings. func ApplyKnownAccountMemory( accounts []missionentity.SimilarAccount, known map[string]accountentity.KnownAccountProfile, ) []missionentity.SimilarAccount { if len(accounts) == 0 || len(known) == 0 { return accounts } out := make([]missionentity.SimilarAccount, len(accounts)) copy(out, accounts) for i := range out { key := strings.ToLower(strings.TrimSpace(out[i].Username)) if key == "" { continue } profile, ok := known[key] if !ok { continue } if profile.Status == accountentity.KnownAccountStatusExcluded { out[i].Status = missionentity.SimilarAccountStatusExcluded } if len(profile.Missions) >= 2 || profile.SeenCount >= 2 { if out[i].Confidence != "high" { out[i].Confidence = "high" } } } return out } // MergeKnownAccountsFromScan upserts similar-account sightings into the account-level // known_accounts map. func MergeKnownAccountsFromScan( prev map[string]accountentity.KnownAccountProfile, accounts []missionentity.SimilarAccount, missionID, personaID string, tags []string, now int64, ) map[string]accountentity.KnownAccountProfile { out := map[string]accountentity.KnownAccountProfile{} for key, item := range prev { out[key] = item } for _, acc := range accounts { user := strings.TrimSpace(acc.Username) if user == "" { continue } key := strings.ToLower(user) profile := out[key] if profile.FirstSeenAt == 0 { profile.FirstSeenAt = now } profile.LastSeenAt = now profile.Missions = appendUnique(profile.Missions, missionID) profile.PersonaIds = appendUnique(profile.PersonaIds, personaID) for _, tag := range tags { profile.Tags = appendUnique(profile.Tags, tag) } prevCount := profile.SeenCount if prevCount < 0 { prevCount = 0 } eng := float64(acc.EngagementScore) if prevCount == 0 { profile.AvgEngagement = eng } else { profile.AvgEngagement = (profile.AvgEngagement*float64(prevCount) + eng) / float64(prevCount+1) } profile.SeenCount = prevCount + 1 switch strings.TrimSpace(acc.Status) { case missionentity.SimilarAccountStatusExcluded: profile.Status = accountentity.KnownAccountStatusExcluded case missionentity.SimilarAccountStatusRecommended: if profile.Status == accountentity.KnownAccountStatusExcluded { profile.Status = "" } } out[key] = profile } return out } func appendUnique(items []string, value string) []string { value = strings.TrimSpace(value) if value == "" { return items } for _, item := range items { if item == value { return items } } return append(items, value) }