114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
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
|
|
}
|