main.go aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m9s
All checks were successful
release-tag / release-image (push) Successful in 2m9s
This commit is contained in:
216
main.go
216
main.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -15,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ===== Defaults / Names (pro Guild per Commands konfigurierbar) =====
|
// ===== Defaults / Names (pro Guild per Commands konfigurierbar) =====
|
||||||
const (
|
var (
|
||||||
pollInterval = 15 * time.Second
|
pollInterval = 15 * time.Second
|
||||||
defaultLobbyName = "➕ Erstelle privaten Raum"
|
defaultLobbyName = "➕ Erstelle privaten Raum"
|
||||||
defaultCategory = "Private Räume"
|
defaultCategory = "Private Räume"
|
||||||
@@ -29,19 +30,12 @@ var dbPath = func() string {
|
|||||||
return "guild_config.db"
|
return "guild_config.db"
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Pfad zur Persistenz-Datei (überschreibbar via CONFIG_PATH)
|
|
||||||
/*var configPath = func() string {
|
|
||||||
if v := os.Getenv("CONFIG_PATH"); v != "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return "guild_config.json"
|
|
||||||
}()*/
|
|
||||||
|
|
||||||
// ===== Per-Guild Config (in-memory) =====
|
// ===== Per-Guild Config (in-memory) =====
|
||||||
type GuildConfig struct {
|
type GuildConfig struct {
|
||||||
LobbyName string `json:"lobby_name"`
|
LobbyName string `json:"lobby_name"`
|
||||||
CategoryName string `json:"category_name"`
|
CategoryName string `json:"category_name"`
|
||||||
TimeoutMin int `json:"timeout_min"`
|
TimeoutMin int `json:"timeout_min"`
|
||||||
|
Language string `json:"language"` // Neue Eigenschaft für Sprache
|
||||||
}
|
}
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
@@ -50,6 +44,36 @@ var db *sql.DB
|
|||||||
var guildCfgs sync.Map // Für Guild-Konfigurationen
|
var guildCfgs sync.Map // Für Guild-Konfigurationen
|
||||||
var createdCmds sync.Map // Für erstellte Commands
|
var createdCmds sync.Map // Für erstellte Commands
|
||||||
|
|
||||||
|
// Sprache für eine Guild setzen
|
||||||
|
func setLanguage(guildID, language string) error {
|
||||||
|
cfg := getCfg(guildID)
|
||||||
|
cfg.Language = language
|
||||||
|
return saveGuildCfgs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprachabruf
|
||||||
|
func getLanguage(guildID string) string {
|
||||||
|
cfg := getCfg(guildID)
|
||||||
|
return cfg.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLanguageColumnIfNotExists() {
|
||||||
|
// Überprüfen, ob die Spalte bereits existiert
|
||||||
|
_, err := db.Exec("ALTER TABLE guild_config ADD COLUMN language TEXT DEFAULT 'de'")
|
||||||
|
if err != nil && !isColumnAlreadyExistsError(err) {
|
||||||
|
log.Fatalf("Fehler beim Hinzufügen der 'language' Spalte: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Println("Spalte 'language' wurde hinzugefügt oder existiert bereits.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion zur Fehlerbehandlung, um zu überprüfen, ob der Fehler auf eine bereits vorhandene Spalte hinweist
|
||||||
|
func isColumnAlreadyExistsError(err error) bool {
|
||||||
|
// Dies ist ein spezifischer Fehlercode für SQLite, der darauf hinweist, dass die Spalte bereits existiert
|
||||||
|
// Der Fehlercode lautet "sqlite3: table ... already has column ..." (in verschiedenen Varianten)
|
||||||
|
return err != nil && err.Error() == "SQLITE_ERROR: duplicate column name: language"
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize DB
|
// Initialize DB
|
||||||
func initDB() {
|
func initDB() {
|
||||||
var err error
|
var err error
|
||||||
@@ -58,12 +82,16 @@ func initDB() {
|
|||||||
log.Fatalf("Datenbank-Fehler: %v", err)
|
log.Fatalf("Datenbank-Fehler: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Neue Spalte für die Sprache hinzufügen, wenn sie noch nicht existiert
|
||||||
|
addLanguageColumnIfNotExists()
|
||||||
|
|
||||||
// Tabelle erstellen, falls sie noch nicht existiert
|
// Tabelle erstellen, falls sie noch nicht existiert
|
||||||
createTableSQL := `CREATE TABLE IF NOT EXISTS guild_config (
|
createTableSQL := `CREATE TABLE IF NOT EXISTS guild_config (
|
||||||
guild_id TEXT PRIMARY KEY,
|
guild_id TEXT PRIMARY KEY,
|
||||||
lobby_name TEXT,
|
lobby_name TEXT,
|
||||||
category_name TEXT,
|
category_name TEXT,
|
||||||
timeout_min INTEGER
|
timeout_min INTEGER,
|
||||||
|
language TEXT DEFAULT 'de' -- Neue Spalte für Sprache hinzufügen
|
||||||
);`
|
);`
|
||||||
_, err = db.Exec(createTableSQL)
|
_, err = db.Exec(createTableSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -78,31 +106,32 @@ func closeDB() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Laden der Guild-Konfiguration aus der Datenbank
|
// Laden der Guild-Konfiguration mit Sprache
|
||||||
func loadGuildCfgs() error {
|
func loadGuildCfgs() error {
|
||||||
rows, err := db.Query("SELECT guild_id, lobby_name, category_name, timeout_min FROM guild_config")
|
rows, err := db.Query("SELECT guild_id, lobby_name, category_name, timeout_min, language FROM guild_config")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var guildID, lobbyName, categoryName string
|
var guildID, lobbyName, categoryName, language string
|
||||||
var timeoutMin int
|
var timeoutMin int
|
||||||
if err := rows.Scan(&guildID, &lobbyName, &categoryName, &timeoutMin); err != nil {
|
if err := rows.Scan(&guildID, &lobbyName, &categoryName, &timeoutMin, &language); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
guildCfgs.Store(guildID, &GuildConfig{
|
guildCfgs.Store(guildID, &GuildConfig{
|
||||||
LobbyName: lobbyName,
|
LobbyName: lobbyName,
|
||||||
CategoryName: categoryName,
|
CategoryName: categoryName,
|
||||||
TimeoutMin: timeoutMin,
|
TimeoutMin: timeoutMin,
|
||||||
|
Language: language, // Sprache laden
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speichern der Guild-Konfiguration in die Datenbank
|
// Speichern der Guild-Konfiguration mit Sprache
|
||||||
func saveGuildCfgs() error {
|
func saveGuildCfgs() error {
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -115,14 +144,15 @@ func saveGuildCfgs() error {
|
|||||||
cfg := value.(*GuildConfig)
|
cfg := value.(*GuildConfig)
|
||||||
|
|
||||||
_, err := tx.Exec(`
|
_, err := tx.Exec(`
|
||||||
INSERT INTO guild_config (guild_id, lobby_name, category_name, timeout_min)
|
INSERT INTO guild_config (guild_id, lobby_name, category_name, timeout_min, language)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
ON CONFLICT(guild_id)
|
ON CONFLICT(guild_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
lobby_name = excluded.lobby_name,
|
lobby_name = excluded.lobby_name,
|
||||||
category_name = excluded.category_name,
|
category_name = excluded.category_name,
|
||||||
timeout_min = excluded.timeout_min
|
timeout_min = excluded.timeout_min,
|
||||||
`, guildID, cfg.LobbyName, cfg.CategoryName, cfg.TimeoutMin)
|
language = excluded.language -- Sprache speichern
|
||||||
|
`, guildID, cfg.LobbyName, cfg.CategoryName, cfg.TimeoutMin, cfg.Language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Speichern der Guild-Konfiguration: %v", err)
|
log.Printf("Fehler beim Speichern der Guild-Konfiguration: %v", err)
|
||||||
}
|
}
|
||||||
@@ -139,17 +169,19 @@ func getCfg(guildID string) *GuildConfig {
|
|||||||
return cfg.(*GuildConfig)
|
return cfg.(*GuildConfig)
|
||||||
}
|
}
|
||||||
var guildCfg GuildConfig
|
var guildCfg GuildConfig
|
||||||
err := db.QueryRow("SELECT lobby_name, category_name, timeout_min FROM guild_config WHERE guild_id = ?", guildID).Scan(&guildCfg.LobbyName, &guildCfg.CategoryName, &guildCfg.TimeoutMin)
|
err := db.QueryRow("SELECT lobby_name, category_name, timeout_min, language FROM guild_config WHERE guild_id = ?", guildID).Scan(&guildCfg.LobbyName, &guildCfg.CategoryName, &guildCfg.TimeoutMin, &guildCfg.Language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
log.Printf("Guild-Konfiguration für %s nicht gefunden, verwenden der Standardwerte", guildID)
|
log.Printf("Guild-Konfiguration für %s nicht gefunden, verwenden der Standardwerte", guildID)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Fehler beim Abrufen der Guild-Konfiguration für %s: %v", guildID, err)
|
log.Printf("Fehler beim Abrufen der Guild-Konfiguration für %s: %v", guildID, err)
|
||||||
}
|
}
|
||||||
|
// Standardwerte verwenden
|
||||||
guildCfg = GuildConfig{
|
guildCfg = GuildConfig{
|
||||||
LobbyName: defaultLobbyName,
|
LobbyName: defaultLobbyName,
|
||||||
CategoryName: defaultCategory,
|
CategoryName: defaultCategory,
|
||||||
TimeoutMin: envTimeoutDefault(1),
|
TimeoutMin: envTimeoutDefault(1),
|
||||||
|
Language: "de", // Standard-Sprache ist Deutsch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guildCfgs.Store(guildID, &guildCfg)
|
guildCfgs.Store(guildID, &guildCfg)
|
||||||
@@ -619,7 +651,6 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
|
|||||||
|
|
||||||
// speichern
|
// speichern
|
||||||
getCfg(guildID).TimeoutMin = int(minutes)
|
getCfg(guildID).TimeoutMin = int(minutes)
|
||||||
log.Println("1-0815")
|
|
||||||
if err := saveGuildCfgs(); err != nil {
|
if err := saveGuildCfgs(); err != nil {
|
||||||
log.Printf("saveGuildCfgs failed: %v", err)
|
log.Printf("saveGuildCfgs failed: %v", err)
|
||||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
@@ -632,8 +663,6 @@ 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,
|
||||||
@@ -735,6 +764,59 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case "setlanguage":
|
||||||
|
// Admin-Check zuerst
|
||||||
|
if !isGuildAdmin(s, guildID, i.User, i.Member) {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "❌ Nur Administratoren dürfen das.",
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option "language" lesen
|
||||||
|
var language string
|
||||||
|
for _, o := range data.Options {
|
||||||
|
if o.Name == "language" {
|
||||||
|
language = o.StringValue()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if language == "" {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "Bitte gib eine gültige Sprache an (z.B. 'de', 'en').",
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichern
|
||||||
|
if err := setLanguage(guildID, language); err != nil {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "⚠️ Fehler beim Speichern der Sprache.",
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Antwort <= 3s
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: fmt.Sprintf("✅ Sprache auf '%s' gesetzt.", language),
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,7 +838,7 @@ func onGuildCreate(s *discordgo.Session, g *discordgo.GuildCreate) {
|
|||||||
commands = append(commands.([]*discordgo.ApplicationCommand), c)
|
commands = append(commands.([]*discordgo.ApplicationCommand), c)
|
||||||
createdCmds.Store(g.ID, commands)
|
createdCmds.Store(g.ID, commands)
|
||||||
|
|
||||||
log.Printf("Command-Registrierung in %s", g.Name)
|
log.Printf("Registriere Command %s in Guild %s", cmd.Name, g.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,22 +891,22 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "setlanguage",
|
||||||
|
Description: "Setzt die Sprache für diese Guild.",
|
||||||
|
DefaultMemberPermissions: &adminPerm,
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "language",
|
||||||
|
Description: "Die Sprache, die gesetzt werden soll (z.B. 'de', 'en').",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
/*func registerCommandsForGuild(s *discordgo.Session, guildID string) {
|
|
||||||
appID := s.State.User.ID
|
|
||||||
cmds, err := s.ApplicationCommandBulkOverwrite(appID, guildID, slashCommands)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("BulkOverwrite %s failed: %v", guildID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfgMu.Lock()
|
|
||||||
createdCmds[guildID] = cmds
|
|
||||||
cfgMu.Unlock()
|
|
||||||
//fmt.Println(createdCmds)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// ===== main: Multi-Guild, pro Guild registrieren =====
|
// ===== main: Multi-Guild, pro Guild registrieren =====
|
||||||
func main() {
|
func main() {
|
||||||
initDB()
|
initDB()
|
||||||
@@ -864,9 +946,9 @@ func main() {
|
|||||||
createdCmds.Range(func(key, value interface{}) bool {
|
createdCmds.Range(func(key, value interface{}) bool {
|
||||||
for _, c := range value.([]*discordgo.ApplicationCommand) {
|
for _, c := range value.([]*discordgo.ApplicationCommand) {
|
||||||
if delErr := s.ApplicationCommandDelete(appID, key.(string), c.ID); delErr != nil {
|
if delErr := s.ApplicationCommandDelete(appID, key.(string), c.ID); delErr != nil {
|
||||||
log.Printf("Cmd-Delete (%s/%s) fehlgeschlagen: %v", key, c.Name, delErr)
|
log.Printf("Cmd-Delete (%s / %s) fehlgeschlagen: %v", key, c.Name, delErr)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Cmd-Delete (%s/%s) ok", key, c.Name)
|
log.Printf("Cmd-Delete (%s / %s) ok", key, c.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -877,3 +959,59 @@ func main() {
|
|||||||
}
|
}
|
||||||
_ = s.Close()
|
_ = s.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Die Struktur für die Übersetzungen.
|
||||||
|
type TranslationsStruct struct {
|
||||||
|
Language string `json:"language"`
|
||||||
|
Messages map[string]string `json:"messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporäre Struktur zur Deserialisierung.
|
||||||
|
type TranslationFile struct {
|
||||||
|
Translations []TranslationsStruct `json:"translations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Globale Variable für die Übersetzungen
|
||||||
|
var translations map[string]map[string]string
|
||||||
|
|
||||||
|
// Funktion zur Deserialisierung und Umwandlung in map[string]map[string]string
|
||||||
|
func loadTranslations(jsonData string) (map[string]map[string]string, error) {
|
||||||
|
var translationFile TranslationFile
|
||||||
|
|
||||||
|
// Deserialisierung des JSON in die Struktur
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &translationFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Umwandlung der Struktur in eine Map
|
||||||
|
result := make(map[string]map[string]string)
|
||||||
|
for _, trans := range translationFile.Translations {
|
||||||
|
result[trans.Language] = trans.Messages
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zur Deserialisierung und Umwandlung in map[string]map[string]string
|
||||||
|
func loadTranslationsFromFile(filename string) (map[string]map[string]string, error) {
|
||||||
|
// Dateiinhalt lesen
|
||||||
|
fileData, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fehler beim lesen der datei: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var translationFile TranslationFile
|
||||||
|
|
||||||
|
// Deserialisierung des JSON in die Struktur
|
||||||
|
if err := json.Unmarshal(fileData, &translationFile); err != nil {
|
||||||
|
return nil, fmt.Errorf("fehler beim deserialisieren der json-daten: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Umwandlung der Struktur in eine Map
|
||||||
|
result := make(map[string]map[string]string)
|
||||||
|
for _, trans := range translationFile.Translations {
|
||||||
|
result[trans.Language] = trans.Messages
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user