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 := strings.ToLower(strings.TrimSpace(query.Query)) ids := make([]string, 0, len(unique)) for id := range unique { if queryText == "" || strings.Contains(strings.ToLower(id), queryText) || strings.Contains(strings.ToLower(unique[id].DisplayName), queryText) { ids = append(ids, id) } } sort.Strings(ids) limit := query.Limit if limit <= 0 || limit > len(ids) { limit = len(ids) } contacts := make([]Contact, 0, limit) for _, id := range ids[:limit] { contacts = append(contacts, unique[id]) } return SearchContactsResult{Contacts: contacts} } func addMessageContact(unique map[string]Contact, id string) { trimmed := strings.TrimSpace(id) if trimmed == "" { return } if _, exists := unique[trimmed]; exists { return } unique[trimmed] = Contact{ ContactID: trimmed, DisplayName: trimmed, Account: trimmed, Source: "local_readonly", Confidence: 0.6, RawRef: "message_jid", } }