mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Add horoscope, !time @user, profile achievements; fix mention detection and multiple bugs
- Add !horoscope command and daily horoscope digest using Free Horoscope API - Enhance !time to support @user timezone lookups - Add profile completeness achievements (birthday, timezone, both set) - Fix Matrix mention detection in reputation and LLM passive plugins - Fix !whois reputation query (reason string mismatch and calculation) - Fix !time IANA case-sensitivity and username/city collision - Fix timezone default to empty string for achievement detection - Scope all leaderboards to room members (XP, rep, potty, insult, first, rankings, emoji) - Fix XP rank calculation to handle tied scores correctly - Replace skull emoji with bomb for bot insult reactions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,9 +16,13 @@ import (
|
||||
|
||||
var thankRe = regexp.MustCompile(`(?i)\b(thanks|thank\s+you|thankyou|thx|ty|tysm|tyvm)\b`)
|
||||
|
||||
// userMentionRe matches Matrix user IDs like @user:server.tld
|
||||
// userMentionRe matches Matrix user IDs like @user:server.tld in plain text.
|
||||
var userMentionRe = regexp.MustCompile(`@[a-zA-Z0-9._=-]+:[a-zA-Z0-9.-]+`)
|
||||
|
||||
// matrixToMentionRe extracts Matrix user IDs from HTML formatted_body mentions.
|
||||
// Element and most clients format mentions as: <a href="https://matrix.to/#/@user:server">Name</a>
|
||||
var matrixToMentionRe = regexp.MustCompile(`https://matrix\.to/#/(@[a-zA-Z0-9._=-]+:[a-zA-Z0-9.-]+)`)
|
||||
|
||||
// ReputationPlugin tracks gratitude and awards reputation XP.
|
||||
type ReputationPlugin struct {
|
||||
Base
|
||||
@@ -63,8 +67,8 @@ func (p *ReputationPlugin) OnMessage(ctx MessageContext) error {
|
||||
}
|
||||
|
||||
func (p *ReputationPlugin) handleThank(ctx MessageContext) error {
|
||||
// Find mentioned users
|
||||
mentions := userMentionRe.FindAllString(ctx.Body, -1)
|
||||
// Find mentioned users from both plain text and HTML formatted body
|
||||
mentions := extractMentions(ctx)
|
||||
if len(mentions) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -125,9 +129,8 @@ func (p *ReputationPlugin) handleRep(ctx MessageContext) error {
|
||||
target := ctx.Sender
|
||||
args := p.GetArgs(ctx.Body, "rep")
|
||||
if args != "" {
|
||||
cleaned := strings.TrimSpace(strings.TrimPrefix(args, "@"))
|
||||
if cleaned != "" {
|
||||
target = id.UserID(cleaned)
|
||||
if resolved, ok := p.ResolveUser(args); ok {
|
||||
target = resolved
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +153,13 @@ func (p *ReputationPlugin) handleRep(ctx MessageContext) error {
|
||||
}
|
||||
|
||||
func (p *ReputationPlugin) handleRepboard(ctx MessageContext) error {
|
||||
members := p.RoomMembers(ctx.RoomID)
|
||||
|
||||
d := db.Get()
|
||||
rows, err := d.Query(
|
||||
`SELECT user_id, SUM(amount) as total
|
||||
FROM xp_log WHERE reason = 'reputation'
|
||||
GROUP BY user_id ORDER BY total DESC LIMIT 10`,
|
||||
GROUP BY user_id ORDER BY total DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
slog.Error("rep: repboard query", "err", err)
|
||||
@@ -167,12 +172,15 @@ func (p *ReputationPlugin) handleRepboard(ctx MessageContext) error {
|
||||
|
||||
medals := []string{"🥇", "🥈", "🥉"}
|
||||
i := 0
|
||||
for rows.Next() {
|
||||
for rows.Next() && i < 10 {
|
||||
var userID string
|
||||
var totalXP int
|
||||
if err := rows.Scan(&userID, &totalXP); err != nil {
|
||||
continue
|
||||
}
|
||||
if members != nil && !members[id.UserID(userID)] {
|
||||
continue
|
||||
}
|
||||
repCount := totalXP / 5
|
||||
prefix := fmt.Sprintf("#%d", i+1)
|
||||
if i < len(medals) {
|
||||
@@ -188,3 +196,33 @@ func (p *ReputationPlugin) handleRepboard(ctx MessageContext) error {
|
||||
|
||||
return p.SendReply(ctx.RoomID, ctx.EventID, sb.String())
|
||||
}
|
||||
|
||||
// extractMentions pulls Matrix user IDs from both the plain text body and
|
||||
// the HTML formatted_body (where most clients put actual @user:server mentions).
|
||||
func extractMentions(ctx MessageContext) []string {
|
||||
seen := make(map[string]bool)
|
||||
var result []string
|
||||
|
||||
// Check plain text body for raw @user:server mentions
|
||||
for _, m := range userMentionRe.FindAllString(ctx.Body, -1) {
|
||||
if !seen[m] {
|
||||
seen[m] = true
|
||||
result = append(result, m)
|
||||
}
|
||||
}
|
||||
|
||||
// Check HTML formatted_body for matrix.to mention links
|
||||
if ctx.Event != nil {
|
||||
content := ctx.Event.Content.AsMessage()
|
||||
if content != nil && content.FormattedBody != "" {
|
||||
for _, match := range matrixToMentionRe.FindAllStringSubmatch(content.FormattedBody, -1) {
|
||||
if len(match) > 1 && !seen[match[1]] {
|
||||
seen[match[1]] = true
|
||||
result = append(result, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user