main.go aktualisiert
Some checks failed
release-tag / release-image (push) Failing after 52s

This commit is contained in:
2025-08-10 19:44:42 +00:00
parent 04025565ff
commit 396ee0398d

203
main.go
View File

@@ -1,7 +1,7 @@
package main package main
import ( import (
"encoding/json" "database/sql"
"fmt" "fmt"
"log" "log"
"os" "os"
@@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
_ "github.com/mattn/go-sqlite3" // SQLite3-Treiber importieren
) )
// ===== Defaults / Names (pro Guild per Commands konfigurierbar) ===== // ===== Defaults / Names (pro Guild per Commands konfigurierbar) =====
@@ -20,53 +21,21 @@ const (
defaultCategory = "Private Räume" defaultCategory = "Private Räume"
) )
// Pfad zur SQLite-Datenbank
var dbPath = func() string {
if v := os.Getenv("DB_PATH"); v != "" {
return v
}
return "guild_config.db"
}()
// Pfad zur Persistenz-Datei (überschreibbar via CONFIG_PATH) // Pfad zur Persistenz-Datei (überschreibbar via CONFIG_PATH)
var configPath = func() string { /*var configPath = func() string {
if v := os.Getenv("CONFIG_PATH"); v != "" { if v := os.Getenv("CONFIG_PATH"); v != "" {
return v return v
} }
return "guild_config.json" return "guild_config.json"
}() }()*/
// Persistenz der Guild-Konfiguration
func loadGuildCfgs() error {
f, err := os.Open(configPath)
if err != nil {
return err
}
defer f.Close()
var m map[string]*GuildConfig
if err := json.NewDecoder(f).Decode(&m); err != nil {
return err
}
cfgMu.Lock()
for k, v := range m {
guildCfgs[k] = v
}
cfgMu.Unlock()
return nil
}
func saveGuildCfgs() error {
tmp := configPath + ".tmp"
f, err := os.Create(tmp)
if err != nil {
return err
}
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
cfgMu.RLock()
err = enc.Encode(guildCfgs)
cfgMu.RUnlock()
_ = f.Close()
if err != nil {
return err
}
return os.Rename(tmp, configPath)
}
// ===== Per-Guild Config (in-memory) ===== // ===== Per-Guild Config (in-memory) =====
type GuildConfig struct { type GuildConfig struct {
@@ -75,29 +44,121 @@ type GuildConfig struct {
TimeoutMin int `json:"timeout_min"` TimeoutMin int `json:"timeout_min"`
} }
var db *sql.DB
// Initialize DB
func initDB() {
var err error
db, err = sql.Open("sqlite3", dbPath)
if err != nil {
log.Fatalf("Datenbank-Fehler: %v", err)
}
// Tabelle erstellen, falls sie noch nicht existiert
createTableSQL := `CREATE TABLE IF NOT EXISTS guild_config (
guild_id TEXT PRIMARY KEY,
lobby_name TEXT,
category_name TEXT,
timeout_min INTEGER
);`
_, err = db.Exec(createTableSQL)
if err != nil {
log.Fatalf("Fehler beim Erstellen der Tabelle: %v", err)
}
}
// Close DB connection
func closeDB() {
if err := db.Close(); err != nil {
log.Fatalf("Fehler beim Schließen der DB: %v", err)
}
}
// Laden der Guild-Konfiguration aus der Datenbank
func loadGuildCfgs() error {
rows, err := db.Query("SELECT guild_id, lobby_name, category_name, timeout_min FROM guild_config")
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var guildID, lobbyName, categoryName string
var timeoutMin int
if err := rows.Scan(&guildID, &lobbyName, &categoryName, &timeoutMin); err != nil {
return err
}
guildCfgs[guildID] = &GuildConfig{
LobbyName: lobbyName,
CategoryName: categoryName,
TimeoutMin: timeoutMin,
}
}
return nil
}
// Speichern der Guild-Konfiguration in die Datenbank
func saveGuildCfgs() error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
for guildID, cfg := range guildCfgs {
// UPDATE oder INSERT, je nachdem, ob es bereits eine Konfiguration gibt
_, err := tx.Exec(`
INSERT INTO guild_config (guild_id, lobby_name, category_name, timeout_min)
VALUES (?, ?, ?, ?)
ON CONFLICT(guild_id)
DO UPDATE SET
lobby_name = excluded.lobby_name,
category_name = excluded.category_name,
timeout_min = excluded.timeout_min
`, guildID, cfg.LobbyName, cfg.CategoryName, cfg.TimeoutMin)
if err != nil {
return err
}
}
// Transaktion abschließen
return tx.Commit()
}
var ( var (
cfgMu sync.RWMutex cfgMu sync.Mutex
guildCfgs = map[string]*GuildConfig{} guildCfgs = map[string]*GuildConfig{}
createdCmds = map[string][]*discordgo.ApplicationCommand{} // guildID -> cmds createdCmds = map[string][]*discordgo.ApplicationCommand{} // guildID -> cmds
registerOnce sync.Once
) )
func getCfg(guildID string) *GuildConfig { func getCfg(guildID string) *GuildConfig {
cfgMu.RLock() cfgMu.Lock()
c, ok := guildCfgs[guildID] c, ok := guildCfgs[guildID]
cfgMu.RUnlock() cfgMu.Unlock()
if ok { if ok {
return c return c
} }
cfgMu.Lock()
defer cfgMu.Unlock() // Falls nicht vorhanden, Daten aus DB laden
c = &GuildConfig{ var guildCfg GuildConfig
LobbyName: defaultLobbyName, err := db.QueryRow("SELECT lobby_name, category_name, timeout_min FROM guild_config WHERE guild_id = ?", guildID).Scan(&guildCfg.LobbyName, &guildCfg.CategoryName, &guildCfg.TimeoutMin)
CategoryName: defaultCategory, if err != nil {
TimeoutMin: envTimeoutDefault(60), if err != sql.ErrNoRows {
log.Printf("Fehler beim Abrufen der Guild-Konfiguration: %v", err)
}
// Fallback-Werte, falls die Guild nicht in der DB existiert
guildCfg = GuildConfig{
LobbyName: defaultLobbyName,
CategoryName: defaultCategory,
TimeoutMin: envTimeoutDefault(1),
}
} }
guildCfgs[guildID] = c
return c cfgMu.Lock()
guildCfgs[guildID] = &guildCfg
cfgMu.Unlock()
return &guildCfg
} }
func envTimeoutDefault(def int) int { func envTimeoutDefault(def int) int {
@@ -541,8 +602,8 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
}) })
case "settimeout": case "settimeout":
// Admin-Check zuerst
if !isGuildAdmin(s, guildID, i.User, i.Member) { if !isGuildAdmin(s, guildID, i.User, i.Member) {
log.Println("User is not an admin")
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource, Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{ Data: &discordgo.InteractionResponseData{
@@ -553,7 +614,6 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
return return
} }
// minutes lesen
minutes := int64(getCfg(guildID).TimeoutMin) minutes := int64(getCfg(guildID).TimeoutMin)
for _, o := range data.Options { for _, o := range data.Options {
if o.Name == "minutes" { if o.Name == "minutes" {
@@ -564,9 +624,12 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
minutes = 1 minutes = 1
} }
log.Printf("Setting timeout for %d minutes", minutes)
// speichern // speichern
cfgMu.Lock() cfgMu.Lock()
getCfg(guildID).TimeoutMin = int(minutes) getCfg(guildID).TimeoutMin = int(minutes)
log.Println("1-0815")
cfgMu.Unlock() cfgMu.Unlock()
if err := saveGuildCfgs(); err != nil { if err := saveGuildCfgs(); err != nil {
log.Printf("saveGuildCfgs failed: %v", err) log.Printf("saveGuildCfgs failed: %v", err)
@@ -580,6 +643,8 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
return return
} }
log.Println("2-0815")
// Antwort <= 3s // Antwort <= 3s
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource, Type: discordgo.InteractionResponseChannelMessageWithSource,
@@ -698,6 +763,7 @@ func onGuildCreate(s *discordgo.Session, g *discordgo.GuildCreate) {
cfgMu.Lock() cfgMu.Lock()
createdCmds[g.ID] = append(createdCmds[g.ID], c) createdCmds[g.ID] = append(createdCmds[g.ID], c)
cfgMu.Unlock() cfgMu.Unlock()
log.Printf("Command-Registrierung in %s", g.Name)
} }
} }
@@ -753,7 +819,7 @@ var (
} }
) )
func registerCommandsForGuild(s *discordgo.Session, guildID string) { /*func registerCommandsForGuild(s *discordgo.Session, guildID string) {
appID := s.State.User.ID appID := s.State.User.ID
cmds, err := s.ApplicationCommandBulkOverwrite(appID, guildID, slashCommands) cmds, err := s.ApplicationCommandBulkOverwrite(appID, guildID, slashCommands)
if err != nil { if err != nil {
@@ -763,18 +829,21 @@ func registerCommandsForGuild(s *discordgo.Session, guildID string) {
cfgMu.Lock() cfgMu.Lock()
createdCmds[guildID] = cmds createdCmds[guildID] = cmds
cfgMu.Unlock() cfgMu.Unlock()
} //fmt.Println(createdCmds)
}*/
// ===== main: Multi-Guild, pro Guild registrieren ===== // ===== main: Multi-Guild, pro Guild registrieren =====
func main() { func main() {
initDB()
token := os.Getenv("DISCORD_TOKEN") token := os.Getenv("DISCORD_TOKEN")
token = "MTQwMzg1MTM5NDQ1MjI5MTU4NA.GVi04l.qjraLIbFdi_N49UcSUv_BqK89ihb6xXY648J7A"
if token == "" { if token == "" {
log.Fatal("Bitte setze DISCORD_TOKEN") log.Fatal("Bitte setze DISCORD_TOKEN")
} }
// Persistente Konfiguration laden (optional) // Persistente Konfiguration laden (optional)
if err := loadGuildCfgs(); err != nil { if err := loadGuildCfgs(); err != nil {
log.Printf("Hinweis: Konnte %s nicht laden (%v). Starte mit Defaults.", configPath, err) log.Printf("Hinweis: Konnte %s nicht laden (%v). Starte mit Defaults.", dbPath, err)
} }
s, err := discordgo.New("Bot " + token) s, err := discordgo.New("Bot " + token)
@@ -786,16 +855,16 @@ func main() {
s.AddHandler(onVoiceStateUpdate) s.AddHandler(onVoiceStateUpdate)
s.AddHandler(onGuildCreate) s.AddHandler(onGuildCreate)
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { /*s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
for _, g := range s.State.Guilds { for _, g := range s.State.Guilds {
registerCommandsForGuild(s, g.ID) registerCommandsForGuild(s, g.ID)
} }
}) })
s.AddHandler(func(s *discordgo.Session, g *discordgo.GuildCreate) { s.AddHandler(func(s *discordgo.Session, g *discordgo.GuildCreate) {
registerCommandsForGuild(s, g.ID) registerCommandsForGuild(s, g.ID)
}) })*/
s.AddHandler(onInteractionCreate("")) s.AddHandler(onInteractionCreate(""))
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { /*s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
log.Printf("Eingeloggt als %s", r.User.Username) log.Printf("Eingeloggt als %s", r.User.Username)
registerOnce.Do(func() { registerOnce.Do(func() {
appID := s.State.User.ID appID := s.State.User.ID
@@ -812,7 +881,7 @@ func main() {
} }
log.Printf("Defaults → Lobby: %q | Kategorie: %q | Timeout: %d min", defaultLobbyName, defaultCategory, envTimeoutDefault(60)) log.Printf("Defaults → Lobby: %q | Kategorie: %q | Timeout: %d min", defaultLobbyName, defaultCategory, envTimeoutDefault(60))
}) })
}) })*/
if err := s.Open(); err != nil { if err := s.Open(); err != nil {
log.Fatalf("Gateway-Start fehlgeschlagen: %v", err) log.Fatalf("Gateway-Start fehlgeschlagen: %v", err)
@@ -829,6 +898,8 @@ func main() {
for _, c := range cmds { for _, c := range cmds {
if delErr := s.ApplicationCommandDelete(appID, guildID, c.ID); delErr != nil { if delErr := s.ApplicationCommandDelete(appID, guildID, c.ID); delErr != nil {
log.Printf("Cmd-Delete (%s/%s) fehlgeschlagen: %v", guildID, c.Name, delErr) log.Printf("Cmd-Delete (%s/%s) fehlgeschlagen: %v", guildID, c.Name, delErr)
} else {
log.Printf("Cmd-Delete (%s/%s) ok", guildID, c.Name)
} }
} }
} }