Updated to Multi-Lobby-Service
This commit is contained in:
226
main.go
226
main.go
@@ -37,6 +37,52 @@ type GuildConfig struct {
|
|||||||
Language string `json:"language"` // Neue Eigenschaft für Sprache
|
Language string `json:"language"` // Neue Eigenschaft für Sprache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LobbyRule struct {
|
||||||
|
LobbyName string
|
||||||
|
CategoryName string
|
||||||
|
TimeoutMin int
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLobbyRules(guildID string) ([]LobbyRule, error) {
|
||||||
|
rows, err := db.Query(`SELECT lobby_name, category_name, timeout_min FROM lobby_rule WHERE guild_id = ?`, guildID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var rules []LobbyRule
|
||||||
|
for rows.Next() {
|
||||||
|
var r LobbyRule
|
||||||
|
if err := rows.Scan(&r.LobbyName, &r.CategoryName, &r.TimeoutMin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rules = append(rules, r)
|
||||||
|
}
|
||||||
|
return rules, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func upsertLobbyRule(guildID, lobby, category string, timeout int) error {
|
||||||
|
if timeout < 1 {
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
_, err := db.Exec(`
|
||||||
|
INSERT INTO lobby_rule (guild_id, lobby_name, category_name, timeout_min)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
ON CONFLICT(guild_id, lobby_name) DO UPDATE SET
|
||||||
|
category_name=excluded.category_name,
|
||||||
|
timeout_min=excluded.timeout_min
|
||||||
|
`, guildID, lobby, category, timeout)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteLobbyRule(guildID, lobby string) (bool, error) {
|
||||||
|
res, err := db.Exec(`DELETE FROM lobby_rule WHERE guild_id=? AND lobby_name=?`, guildID, lobby)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
n, _ := res.RowsAffected()
|
||||||
|
return n > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
|
|
||||||
// ===== Global variables for sync.Map =====
|
// ===== Global variables for sync.Map =====
|
||||||
@@ -56,6 +102,25 @@ func getLanguage(guildID string) string {
|
|||||||
return cfg.Language
|
return cfg.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// neue Tabelle für mehrere Lobby-Regeln pro Guild
|
||||||
|
func ensureLobbyRuleTable() {
|
||||||
|
const create = `
|
||||||
|
CREATE TABLE IF NOT EXISTS lobby_rule (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
guild_id TEXT NOT NULL,
|
||||||
|
lobby_name TEXT NOT NULL,
|
||||||
|
category_name TEXT NOT NULL,
|
||||||
|
timeout_min INTEGER NOT NULL DEFAULT 60,
|
||||||
|
UNIQUE (guild_id, lobby_name)
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_lobby_rule_guild ON lobby_rule(guild_id);
|
||||||
|
`
|
||||||
|
_, err := db.Exec(create)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Fehler beim Erstellen lobby_rule: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Funktion zum Hinzufügen der 'language' Spalte, falls sie nicht existiert
|
// Funktion zum Hinzufügen der 'language' Spalte, falls sie nicht existiert
|
||||||
func addLanguageColumnIfNotExists() {
|
func addLanguageColumnIfNotExists() {
|
||||||
// Überprüfen, ob die Tabelle 'guild_config' existiert
|
// Überprüfen, ob die Tabelle 'guild_config' existiert
|
||||||
@@ -103,6 +168,7 @@ func initDB() {
|
|||||||
|
|
||||||
// Neue Spalte für die Sprache hinzufügen, wenn sie noch nicht existiert
|
// Neue Spalte für die Sprache hinzufügen, wenn sie noch nicht existiert
|
||||||
addLanguageColumnIfNotExists()
|
addLanguageColumnIfNotExists()
|
||||||
|
ensureLobbyRuleTable()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,20 +476,62 @@ func onVoiceStateUpdate(s *discordgo.Session, e *discordgo.VoiceStateUpdate) {
|
|||||||
if e.UserID == "" {
|
if e.UserID == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1) Versuch: Regeln aus DB
|
||||||
|
rules, err := getLobbyRules(e.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("getLobbyRules: %v", err)
|
||||||
|
}
|
||||||
|
if len(rules) > 0 {
|
||||||
|
// checke, ob der Join in eine der konfigurierten Lobbys erfolgte
|
||||||
|
var matched *LobbyRule
|
||||||
|
for idx := range rules {
|
||||||
|
lobbyID := findVoiceChannelIDByName(s, e.GuildID, rules[idx].LobbyName)
|
||||||
|
if lobbyID != "" && e.ChannelID == lobbyID && (e.BeforeUpdate == nil || e.BeforeUpdate.ChannelID != lobbyID) {
|
||||||
|
matched = &rules[idx]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m, _ := s.GuildMember(e.GuildID, e.UserID)
|
||||||
|
if m != nil && m.User.Bot {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
catID, err := findOrCreateCategoryID(s, e.GuildID, matched.CategoryName)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Kategorie: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = createPrivateVCAndMove(s, e.GuildID, e.UserID, safeDisplayName(m), catID, 0, matched.TimeoutMin, e.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("VC/Move: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Fallback: alte Single-Config (deine bisherige Logik)
|
||||||
cfg := getCfg(e.GuildID)
|
cfg := getCfg(e.GuildID)
|
||||||
lobby := findVoiceChannelIDByName(s, e.GuildID, cfg.LobbyName)
|
lobby := findVoiceChannelIDByName(s, e.GuildID, cfg.LobbyName)
|
||||||
if lobby == "" || e.ChannelID != lobby || (e.BeforeUpdate != nil && e.BeforeUpdate.ChannelID == lobby) {
|
if lobby == "" || e.ChannelID != lobby || (e.BeforeUpdate != nil && e.BeforeUpdate.ChannelID == lobby) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := s.GuildMember(e.GuildID, e.UserID)
|
m, err := s.GuildMember(e.GuildID, e.UserID)
|
||||||
if err == nil && m.User.Bot {
|
if err == nil && m.User.Bot {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
catID, err := findOrCreateCategoryID(s, e.GuildID, cfg.CategoryName)
|
catID, err := findOrCreateCategoryID(s, e.GuildID, cfg.CategoryName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Kategorie-Auflösung fehlgeschlagen: %v", err)
|
log.Printf("Kategorie-Auflösung fehlgeschlagen: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = createPrivateVCAndMove(s, e.GuildID, e.UserID, safeDisplayName(m), catID, 0, cfg.TimeoutMin, lobby)
|
_, err = createPrivateVCAndMove(s, e.GuildID, e.UserID, safeDisplayName(m), catID, 0, cfg.TimeoutMin, lobby)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("VC-Erstellung/Move fehlgeschlagen: %v", err)
|
log.Printf("VC-Erstellung/Move fehlgeschlagen: %v", err)
|
||||||
@@ -824,6 +932,100 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case "addlobby":
|
||||||
|
if !isGuildAdmin(s, guildID, i.User, i.Member) {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: "❌ Admins only.", Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var lobby, category string
|
||||||
|
timeout := int64(envTimeoutDefault(60))
|
||||||
|
for _, o := range data.Options {
|
||||||
|
switch o.Name {
|
||||||
|
case "lobby":
|
||||||
|
lobby = o.StringValue()
|
||||||
|
case "category":
|
||||||
|
category = o.StringValue()
|
||||||
|
case "timeout":
|
||||||
|
timeout = o.IntValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lobby == "" || category == "" { /* antworten mit Fehler */
|
||||||
|
}
|
||||||
|
if err := upsertLobbyRule(guildID, lobby, category, int(timeout)); err != nil {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: fmt.Sprintf("⚠️ Failed: %v", err), Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: fmt.Sprintf("✅ Rule saved: lobby \"%s\" → category \"%s\" (timeout %d min).", lobby, category, timeout), Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
|
||||||
|
case "removelobby":
|
||||||
|
if !isGuildAdmin(s, guildID, i.User, i.Member) { /* gleiche Admin-Antwort */
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var lobby string
|
||||||
|
for _, o := range data.Options {
|
||||||
|
if o.Name == "lobby" {
|
||||||
|
lobby = o.StringValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lobby == "" { /* Fehlerantwort */
|
||||||
|
return
|
||||||
|
}
|
||||||
|
removed, err := deleteLobbyRule(guildID, lobby)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: fmt.Sprintf("⚠️ Failed: %v", err), Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := "ℹ️ No rule found."
|
||||||
|
if removed {
|
||||||
|
msg = "✅ Rule removed."
|
||||||
|
}
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: msg, Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
|
||||||
|
case "listlobbies":
|
||||||
|
if !isGuildAdmin(s, guildID, i.User, i.Member) { /* Admin-Antwort */
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rules, err := getLobbyRules(guildID)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: fmt.Sprintf("⚠️ Failed: %v", err), Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(rules) == 0 {
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: "ℹ️ No lobby rules configured (fallback to single-config).", Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// kompakte Ausgabe
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("**Configured lobby rules:**\n")
|
||||||
|
for _, r := range rules {
|
||||||
|
fmt.Fprintf(&b, "• Lobby: `%s` → Category: `%s`, Timeout: `%d min`\n", r.LobbyName, r.CategoryName, r.TimeoutMin)
|
||||||
|
}
|
||||||
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{Content: b.String(), Flags: discordgo.MessageFlagsEphemeral},
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -911,6 +1113,29 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "addlobby",
|
||||||
|
Description: "Adds/updates a lobby rule (lobby → category → timeout)",
|
||||||
|
DefaultMemberPermissions: &adminPerm,
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{Type: discordgo.ApplicationCommandOptionString, Name: "lobby", Description: "Lobby voice channel name", Required: true},
|
||||||
|
{Type: discordgo.ApplicationCommandOptionString, Name: "category", Description: "Category for private rooms", Required: true},
|
||||||
|
{Type: discordgo.ApplicationCommandOptionInteger, Name: "timeout", Description: "Timeout in minutes (>=1)", Required: false, MaxValue: 480},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "removelobby",
|
||||||
|
Description: "Removes a lobby rule by lobby name",
|
||||||
|
DefaultMemberPermissions: &adminPerm,
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{Type: discordgo.ApplicationCommandOptionString, Name: "lobby", Description: "Lobby voice channel name", Required: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "listlobbies",
|
||||||
|
Description: "Lists all lobby rules for this guild",
|
||||||
|
DefaultMemberPermissions: &adminPerm,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -935,7 +1160,6 @@ func main() {
|
|||||||
|
|
||||||
loadTranslationsFromFile(GetENV("TRANSLATIONS_FILE", "./language.json"))
|
loadTranslationsFromFile(GetENV("TRANSLATIONS_FILE", "./language.json"))
|
||||||
token := GetENV("DISCORD_TOKEN", "")
|
token := 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")
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user