main.go aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m8s

This commit is contained in:
2025-08-11 17:38:03 +00:00
parent e28aaa484d
commit c9308c4e25

133
main.go
View File

@@ -271,17 +271,70 @@ func envTimeoutDefault(def int) int {
return def
}
// ===== Helpers: Channel/Kategorie finden =====
// Optional: Cache-Invalidierung bei Channel-Events (empfohlen)
func hookCategoryCacheInvalidation(s *discordgo.Session) {
s.AddHandler(func(_ *discordgo.Session, e *discordgo.ChannelDelete) {
if e.Channel != nil && e.Channel.Type == discordgo.ChannelTypeGuildCategory {
// alle Keys mit dieser ID rauswerfen
catCache.Range(func(k, v interface{}) bool {
if v.(cacheEntry).id == e.ID {
catCache.Delete(k)
}
return true
})
}
})
s.AddHandler(func(_ *discordgo.Session, e *discordgo.ChannelUpdate) {
if e.Channel != nil && e.Channel.Type == discordgo.ChannelTypeGuildCategory {
// Bei Rename nicht perfekt zu erkennen -> TTL sorgt für Refresh
// Optional: hart löschen:
catCache.Range(func(k, v interface{}) bool {
if v.(cacheEntry).id == e.Channel.ID {
catCache.Delete(k)
}
return true
})
}
})
}
// ===== Helpers: Channel/Kategorie finden (mit Cache) =====
func findOrCreateCategoryID(s *discordgo.Session, guildID, name string) (string, error) {
chans, err := s.GuildChannels(guildID)
if err != nil {
return "", err
}
for _, ch := range chans {
if ch.Type == discordgo.ChannelTypeGuildCategory && ch.Name == name {
return ch.ID, nil
key := guildID + "|" + name
// 0) Cache
if v, ok := catCache.Load(key); ok {
ce := v.(cacheEntry)
if time.Since(ce.t) < cacheTTL {
return ce.id, nil
}
}
// 1) State (schnell) vorausgesetzt: s.State.TrackChannels = true (vor s.Open() setzen!)
if g, err := s.State.Guild(guildID); err == nil {
for _, ch := range g.Channels {
if ch.Type == discordgo.ChannelTypeGuildCategory && ch.Name == name {
catCache.Store(key, cacheEntry{ch.ID, time.Now()})
return ch.ID, nil
}
}
}
// 2) REST (Fallback)
chans, err := s.GuildChannels(guildID)
if err == nil {
for _, ch := range chans {
if ch.Type == discordgo.ChannelTypeGuildCategory && ch.Name == name {
catCache.Store(key, cacheEntry{ch.ID, time.Now()})
return ch.ID, nil
}
}
} else {
// wenn selbst Channels holen fehlschlägt, gib den Fehler zurück
return "", err
}
// 3) Nicht gefunden -> anlegen
cat, err := s.GuildChannelCreateComplex(guildID, discordgo.GuildChannelCreateData{
Name: name,
Type: discordgo.ChannelTypeGuildCategory,
@@ -289,30 +342,76 @@ func findOrCreateCategoryID(s *discordgo.Session, guildID, name string) (string,
if err != nil {
return "", err
}
catCache.Store(key, cacheEntry{cat.ID, time.Now()})
return cat.ID, nil
}
var (
vcCache sync.Map // key: guildID+"|"+name → cacheEntry
catCache sync.Map // key: guildID+"|"+name -> cacheEntry
cacheTTL = 12 * time.Hour
)
type cacheEntry struct {
id string
t time.Time
}
// Schneller: erst State, dann REST, plus kleiner TTL-Cache
func findVoiceChannelIDByName(s *discordgo.Session, guildID, name string) string {
key := guildID + "|" + name
if v, ok := vcCache.Load(key); ok {
ce := v.(cacheEntry)
if time.Since(ce.t) < cacheTTL {
return ce.id
}
}
// 1) State (sehr schnell)
if g, err := s.State.Guild(guildID); err == nil {
for _, ch := range g.Channels {
if ch.Type == discordgo.ChannelTypeGuildVoice && ch.Name == name {
vcCache.Store(key, cacheEntry{ch.ID, time.Now()})
return ch.ID
}
}
}
// 2) REST (Fallback, teuer)
chans, err := s.GuildChannels(guildID)
if err != nil {
return ""
}
for _, ch := range chans {
if ch.Type == discordgo.ChannelTypeGuildVoice && ch.Name == name {
vcCache.Store(key, cacheEntry{ch.ID, time.Now()})
return ch.ID
}
}
return ""
}
// Noch schneller: gezielt VoiceState aus dem State ziehen
func findUserVoiceChannelID(s *discordgo.Session, guildID, userID string) string {
g, err := s.State.Guild(guildID)
if err != nil {
g, err = s.Guild(guildID)
if err != nil {
return ""
// 1) Direkt per VoiceState (O(1), wenn im State vorhanden)
if vs, err := s.State.VoiceState(guildID, userID); err == nil && vs != nil && vs.ChannelID != "" {
return vs.ChannelID
}
// 2) State.Guild als Fallback (O(n), aber ohne REST)
if g, err := s.State.Guild(guildID); err == nil {
for _, vs := range g.VoiceStates {
if vs.UserID == userID && vs.ChannelID != "" {
return vs.ChannelID
}
}
}
// 3) REST (letzter Ausweg)
g, err := s.Guild(guildID)
if err != nil {
return ""
}
for _, vs := range g.VoiceStates {
if vs.UserID == userID && vs.ChannelID != "" {
return vs.ChannelID
@@ -1179,6 +1278,14 @@ func main() {
s.AddHandler(onVoiceStateUpdate)
s.AddHandler(onGuildCreate)
s.AddHandler(onInteractionCreate(""))
s.AddHandler(func(_ *discordgo.Session, e *discordgo.ChannelDelete) {
vcCache.Range(func(k, v interface{}) bool {
if v.(cacheEntry).id == e.ID {
vcCache.Delete(k)
}
return true
})
})
if err := s.Open(); err != nil {
log.Fatalf("Gateway-Start fehlgeschlagen: %v", err)