The panel is now one category dropdown and a Create ticket button instead of a button per category. Discord doesn't send the dropdown's value with a button click, so each member's pick is remembered in memory for 15 minutes (per guild and member, capped). Panels posted before keep working until they're reposted. Every ticket embed is orange, closed tickets included. Co-Authored-By: Claude Opus 5.5 <[email protected]>
60 lines
1.4 KiB
Go
60 lines
1.4 KiB
Go
package tickets
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Discord doesn't send a dropdown's value along with a button click, so the
|
|
// panel's category pick is remembered here between the two interactions:
|
|
// per guild and member, for a short while, in memory. A bot restart forgets
|
|
// it, which only means picking the category again.
|
|
const (
|
|
pickTTL = 15 * time.Minute
|
|
maxPicks = 10000 // a ceiling, so a flood of picks can't grow this forever
|
|
)
|
|
|
|
type pickKey struct{ guild, user int64 }
|
|
|
|
type pick struct {
|
|
category int64
|
|
at time.Time
|
|
}
|
|
|
|
var (
|
|
picksMu sync.Mutex
|
|
picks = map[pickKey]pick{}
|
|
now = time.Now
|
|
)
|
|
|
|
// RememberPick records the category a member chose in the panel dropdown.
|
|
func RememberPick(guildID, userID, categoryID int64) {
|
|
picksMu.Lock()
|
|
defer picksMu.Unlock()
|
|
t := now()
|
|
k := pickKey{guildID, userID}
|
|
if _, ok := picks[k]; !ok && len(picks) >= maxPicks {
|
|
for k, p := range picks {
|
|
if t.Sub(p.at) > pickTTL {
|
|
delete(picks, k)
|
|
}
|
|
}
|
|
if len(picks) >= maxPicks {
|
|
return
|
|
}
|
|
}
|
|
picks[k] = pick{categoryID, t}
|
|
}
|
|
|
|
// PickedCategory returns the category a member chose, if they did recently.
|
|
func PickedCategory(guildID, userID int64) (int64, bool) {
|
|
picksMu.Lock()
|
|
defer picksMu.Unlock()
|
|
p, ok := picks[pickKey{guildID, userID}]
|
|
if !ok || now().Sub(p.at) > pickTTL {
|
|
delete(picks, pickKey{guildID, userID})
|
|
return 0, false
|
|
}
|
|
return p.category, true
|
|
}
|