189 lines
4.8 KiB
Go
189 lines
4.8 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strings"
|
|
|
|
"isphere-ai-bridge/internal/isphere"
|
|
"isphere-ai-bridge/internal/msglib"
|
|
)
|
|
|
|
type DisplayEntitySource interface {
|
|
DisplayEntities(ctx context.Context, opts msglib.DisplayEntitiesOptions) ([]msglib.DisplayEntity, error)
|
|
}
|
|
|
|
func displayEntityQueryLimit(limit int) int {
|
|
if limit <= 0 {
|
|
return msglib.DefaultDisplayEntityLimit
|
|
}
|
|
if limit > msglib.MaxDisplayEntityLimit {
|
|
return msglib.MaxDisplayEntityLimit
|
|
}
|
|
return limit
|
|
}
|
|
|
|
func contactsFromDisplayEntities(entities []msglib.DisplayEntity) []isphere.Contact {
|
|
contacts := make([]isphere.Contact, 0, len(entities))
|
|
for _, entity := range entities {
|
|
id := strings.TrimSpace(entity.JID)
|
|
name := strings.TrimSpace(entity.DisplayName)
|
|
if id == "" {
|
|
id = name
|
|
}
|
|
if name == "" {
|
|
name = id
|
|
}
|
|
if id == "" {
|
|
continue
|
|
}
|
|
contacts = append(contacts, isphere.Contact{
|
|
ContactID: id,
|
|
DisplayName: name,
|
|
Account: strings.TrimSpace(entity.JID),
|
|
Source: "msglib_readonly",
|
|
Confidence: entity.Confidence,
|
|
RawRef: "msglib:" + entity.SourceTable,
|
|
})
|
|
}
|
|
return contacts
|
|
}
|
|
|
|
func groupsFromDisplayEntities(entities []msglib.DisplayEntity) []isphere.Group {
|
|
groups := make([]isphere.Group, 0, len(entities))
|
|
for _, entity := range entities {
|
|
id := strings.TrimSpace(entity.JID)
|
|
name := strings.TrimSpace(entity.DisplayName)
|
|
if id == "" {
|
|
id = name
|
|
}
|
|
if name == "" {
|
|
name = id
|
|
}
|
|
if id == "" {
|
|
continue
|
|
}
|
|
groups = append(groups, isphere.Group{
|
|
GroupID: id,
|
|
DisplayName: name,
|
|
Source: "msglib_readonly",
|
|
Confidence: entity.Confidence,
|
|
RawRef: "msglib:" + entity.SourceTable,
|
|
})
|
|
}
|
|
return groups
|
|
}
|
|
|
|
func mergeContacts(primary []isphere.Contact, fallback []isphere.Contact, query string, limit int) isphere.SearchContactsResult {
|
|
seen := map[string]bool{}
|
|
contacts := make([]isphere.Contact, 0, len(primary)+len(fallback))
|
|
for _, contact := range append(primary, fallback...) {
|
|
id := strings.TrimSpace(contact.ContactID)
|
|
if id == "" || seen[strings.ToLower(id)] {
|
|
continue
|
|
}
|
|
seen[strings.ToLower(id)] = true
|
|
contacts = append(contacts, contact)
|
|
}
|
|
sortContactsForQuery(contacts, query)
|
|
if limit > 0 && len(contacts) > limit {
|
|
contacts = contacts[:limit]
|
|
}
|
|
return isphere.SearchContactsResult{Contacts: contacts}
|
|
}
|
|
|
|
func mergeGroups(primary []isphere.Group, fallback []isphere.Group, query string, limit int) isphere.SearchGroupsResult {
|
|
seen := map[string]bool{}
|
|
groups := make([]isphere.Group, 0, len(primary)+len(fallback))
|
|
for _, group := range append(primary, fallback...) {
|
|
id := strings.TrimSpace(group.GroupID)
|
|
if id == "" || seen[strings.ToLower(id)] {
|
|
continue
|
|
}
|
|
seen[strings.ToLower(id)] = true
|
|
groups = append(groups, group)
|
|
}
|
|
sortGroupsForQuery(groups, query)
|
|
if limit > 0 && len(groups) > limit {
|
|
groups = groups[:limit]
|
|
}
|
|
return isphere.SearchGroupsResult{Groups: groups}
|
|
}
|
|
|
|
func sortContactsForQuery(contacts []isphere.Contact, query string) {
|
|
queryText := strings.ToLower(strings.TrimSpace(query))
|
|
sort.Slice(contacts, func(i, j int) bool {
|
|
leftRank := contactRankForQuery(contacts[i], queryText)
|
|
rightRank := contactRankForQuery(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
|
|
})
|
|
}
|
|
|
|
func contactRankForQuery(contact isphere.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
|
|
}
|
|
|
|
func sortGroupsForQuery(groups []isphere.Group, query string) {
|
|
queryText := strings.ToLower(strings.TrimSpace(query))
|
|
sort.Slice(groups, func(i, j int) bool {
|
|
leftRank := groupRankForQuery(groups[i], queryText)
|
|
rightRank := groupRankForQuery(groups[j], queryText)
|
|
if leftRank != rightRank {
|
|
return leftRank < rightRank
|
|
}
|
|
leftID := strings.ToLower(groups[i].GroupID)
|
|
rightID := strings.ToLower(groups[j].GroupID)
|
|
if leftID != rightID {
|
|
return leftID < rightID
|
|
}
|
|
return groups[i].GroupID < groups[j].GroupID
|
|
})
|
|
}
|
|
|
|
func groupRankForQuery(group isphere.Group, queryText string) int {
|
|
if queryText == "" {
|
|
return 0
|
|
}
|
|
values := []string{
|
|
strings.ToLower(group.GroupID),
|
|
strings.ToLower(group.DisplayName),
|
|
}
|
|
for _, value := range values {
|
|
if value == queryText {
|
|
return 0
|
|
}
|
|
}
|
|
for _, value := range values {
|
|
if strings.HasPrefix(value, queryText) {
|
|
return 1
|
|
}
|
|
}
|
|
return 2
|
|
}
|