package isphere import ( "sort" "strings" ) type Contact struct { ContactID string DisplayName string Account string Source string Confidence float64 RawRef string } type SearchContactsQuery struct { Query string Limit int } type SearchContactsResult struct { Contacts []Contact } func SearchContactsFromMessages(messages []Message, query SearchContactsQuery) SearchContactsResult { unique := map[string]Contact{} for _, message := range messages { addMessageContact(unique, message.SenderID) addMessageContact(unique, message.ReceiverID) } queryText := normalizedSearchText(query.Query) contacts := make([]Contact, 0, len(unique)) for _, contact := range unique { if contactMatchesQuery(contact, queryText) { contacts = append(contacts, contact) } } sort.Slice(contacts, func(i, j int) bool { leftRank := contactSearchRank(contacts[i], queryText) rightRank := contactSearchRank(contacts[j], queryText) if leftRank != rightRank { return leftRank < rightRank } leftID := strings.ToLower(contacts[i].ContactID) rightID := strings.ToLower(contacts[j].ContactID) if leftID != rightID { return leftID < rightID } return contacts[i].ContactID < contacts[j].ContactID }) limit := query.Limit if limit <= 0 || limit > len(contacts) { limit = len(contacts) } return SearchContactsResult{Contacts: contacts[:limit]} } func addMessageContact(unique map[string]Contact, id string) { trimmed := strings.TrimSpace(id) if trimmed == "" { return } key := strings.ToLower(trimmed) if _, exists := unique[key]; exists { return } unique[key] = Contact{ ContactID: trimmed, DisplayName: trimmed, Account: trimmed, Source: "local_readonly", Confidence: 0.6, RawRef: "message_jid", } } func normalizedSearchText(value string) string { return strings.ToLower(strings.TrimSpace(value)) } func contactMatchesQuery(contact Contact, queryText string) bool { if queryText == "" { return true } return strings.Contains(strings.ToLower(contact.ContactID), queryText) || strings.Contains(strings.ToLower(contact.DisplayName), queryText) || strings.Contains(strings.ToLower(contact.Account), queryText) } func contactSearchRank(contact Contact, queryText string) int { if queryText == "" { return 0 } values := []string{ strings.ToLower(contact.ContactID), strings.ToLower(contact.DisplayName), strings.ToLower(contact.Account), } for _, value := range values { if value == queryText { return 0 } } for _, value := range values { if strings.HasPrefix(value, queryText) { return 1 } } return 2 }