Files
isphere-ai-bridge/internal/isphere/groups.go
2026-07-10 09:39:18 +08:00

120 lines
2.7 KiB
Go

package isphere
import (
"sort"
"strings"
)
type Group struct {
GroupID string
DisplayName string
Source string
Confidence float64
RawRef string
}
type SearchGroupsQuery struct {
Query string
Limit int
}
type SearchGroupsResult struct {
Groups []Group
}
func SearchGroupsFromMessages(messages []Message, query SearchGroupsQuery) SearchGroupsResult {
unique := map[string]Group{}
for _, message := range messages {
if strings.EqualFold(message.ConversationType, "groupchat") {
addMessageGroup(unique, message.SenderID)
addMessageGroup(unique, message.ReceiverID)
continue
}
if isLikelyGroupJID(message.SenderID) {
addMessageGroup(unique, message.SenderID)
}
if isLikelyGroupJID(message.ReceiverID) {
addMessageGroup(unique, message.ReceiverID)
}
}
queryText := normalizedSearchText(query.Query)
groups := make([]Group, 0, len(unique))
for _, group := range unique {
if groupMatchesQuery(group, queryText) {
groups = append(groups, group)
}
}
sort.Slice(groups, func(i, j int) bool {
leftRank := groupSearchRank(groups[i], queryText)
rightRank := groupSearchRank(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
})
limit := query.Limit
if limit <= 0 || limit > len(groups) {
limit = len(groups)
}
return SearchGroupsResult{Groups: groups[:limit]}
}
func addMessageGroup(unique map[string]Group, id string) {
trimmed := strings.TrimSpace(id)
if trimmed == "" || !isLikelyGroupJID(trimmed) {
return
}
key := strings.ToLower(trimmed)
if _, exists := unique[key]; exists {
return
}
unique[key] = Group{
GroupID: trimmed,
DisplayName: trimmed,
Source: "local_readonly",
Confidence: 0.6,
RawRef: "groupchat_jid",
}
}
func isLikelyGroupJID(id string) bool {
lower := strings.ToLower(strings.TrimSpace(id))
return strings.Contains(lower, "conference") || strings.Contains(lower, "muc") || strings.Contains(lower, "group")
}
func groupMatchesQuery(group Group, queryText string) bool {
if queryText == "" {
return true
}
return strings.Contains(strings.ToLower(group.GroupID), queryText) ||
strings.Contains(strings.ToLower(group.DisplayName), queryText)
}
func groupSearchRank(group 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
}