mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Add tarot readings, encrypted quote wall, display name resolution, and uptime reporting
New features: - !tarot and !tarotspread commands with LLM-powered readings (zodiac-aware) - !quote system with AES-256-GCM encryption at rest, reply-to-save, search, and quoteboard - Shared crypto utility (internal/crypto) for AES-256-GCM encrypt/decrypt/HMAC - ResolveUser now matches display names via room membership as fallback - !vibe and !tldr show bot uptime when insufficient messages buffered Bug fixes: - Fix duplicate quotes table schema that would crash quotewall at runtime - Fix biased random selection in quote retrieval (use rand.Intn) - Fix XP rank calculation to handle tied scores with COUNT(DISTINCT xp) - Scope all leaderboards to room members (repboard, pottyboard, insultboard, firstboard, rankings, emojiboard) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -103,10 +103,11 @@ func (b *Base) RoomMembers(roomID id.RoomID) map[id.UserID]bool {
|
||||
return members
|
||||
}
|
||||
|
||||
// ResolveUser resolves a partial username to a full Matrix user ID.
|
||||
// Accepts any of: "@user:server", "user:server", "user", or partial match.
|
||||
// ResolveUser resolves a partial username or display name to a full Matrix user ID.
|
||||
// Accepts any of: "@user:server", "user:server", "user", display name, or partial match.
|
||||
// Uses room membership to match against display names when a localpart match isn't found.
|
||||
// Returns the matched user ID and true, or empty and false if no unique match.
|
||||
func (b *Base) ResolveUser(input string) (id.UserID, bool) {
|
||||
func (b *Base) ResolveUser(input string, roomIDs ...id.RoomID) (id.UserID, bool) {
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
return "", false
|
||||
@@ -122,7 +123,7 @@ func (b *Base) ResolveUser(input string) (id.UserID, bool) {
|
||||
|
||||
// Strip leading @ for the search
|
||||
query := strings.TrimPrefix(input, "@")
|
||||
query = strings.ToLower(query)
|
||||
queryLower := strings.ToLower(query)
|
||||
|
||||
d := db.Get()
|
||||
rows, err := d.Query(`SELECT user_id FROM user_stats`)
|
||||
@@ -151,27 +152,49 @@ func (b *Base) ResolveUser(input string) (id.UserID, bool) {
|
||||
|
||||
lower := strings.ToLower(localpart)
|
||||
|
||||
if lower == query {
|
||||
if lower == queryLower {
|
||||
exact = append(exact, uid)
|
||||
} else if strings.Contains(lower, query) {
|
||||
} else if strings.Contains(lower, queryLower) {
|
||||
partial = append(partial, uid)
|
||||
}
|
||||
}
|
||||
|
||||
// Exact localpart match — return it (even if multiple servers, prefer first)
|
||||
if len(exact) == 1 {
|
||||
return id.UserID(exact[0]), true
|
||||
}
|
||||
if len(exact) > 1 {
|
||||
// Multiple exact matches across servers — return the first one
|
||||
if len(exact) >= 1 {
|
||||
return id.UserID(exact[0]), true
|
||||
}
|
||||
|
||||
// Single partial match
|
||||
// Single partial localpart match
|
||||
if len(partial) == 1 {
|
||||
return id.UserID(partial[0]), true
|
||||
}
|
||||
|
||||
// Fall back to display name matching via room membership
|
||||
if len(roomIDs) > 0 {
|
||||
resp, err := b.Client.JoinedMembers(context.Background(), roomIDs[0])
|
||||
if err == nil {
|
||||
var dnExact []id.UserID
|
||||
var dnPartial []id.UserID
|
||||
for uid, member := range resp.Joined {
|
||||
if member.DisplayName == "" {
|
||||
continue
|
||||
}
|
||||
dn := strings.ToLower(member.DisplayName)
|
||||
if dn == queryLower {
|
||||
dnExact = append(dnExact, uid)
|
||||
} else if strings.Contains(dn, queryLower) {
|
||||
dnPartial = append(dnPartial, uid)
|
||||
}
|
||||
}
|
||||
if len(dnExact) >= 1 {
|
||||
return dnExact[0], true
|
||||
}
|
||||
if len(dnPartial) == 1 {
|
||||
return dnPartial[0], true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user