diff --git a/main.go b/main.go index 425bcc8..c59c49d 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,9 @@ var dbPath = func() string { return GetENV("DB_PATH", "guild_config.db") }() +// irgendwo global +var presenceGuildID = GetENV("PRESENCE_GUILD_ID", "") + // ===== Per-Guild Config (in-memory) ===== type GuildConfig struct { LobbyName string `json:"lobby_name"` @@ -99,7 +102,7 @@ func setLanguage(guildID, language string) error { // Sprachabruf func getLanguage(guildID string) string { cfg := getCfg(guildID) - fmt.Println("Debug: Language:", cfg.Language) + //fmt.Println("Debug: Language:", cfg.Language) return cfg.Language } @@ -1131,29 +1134,181 @@ func onInteractionCreate(_ string) func(s *discordgo.Session, i *discordgo.Inter Data: &discordgo.InteractionResponseData{Content: b.String(), Flags: discordgo.MessageFlagsEphemeral}, }) + case "setpresence": + // nur erlaubte Guild + if presenceGuildID == "" || i.GuildID != presenceGuildID { + _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "❌ This command is only available on the owner guild.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + return + } + // zusätzlich Admin-Check + if !isGuildAdmin(s, i.GuildID, i.User, i.Member) { + _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "❌ Admins only.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + return + } + + // Options lesen + var mode, text, status, url string + for _, o := range i.ApplicationCommandData().Options { + switch o.Name { + case "mode": + mode = strings.ToLower(o.StringValue()) + case "text": + text = o.StringValue() + case "status": + status = strings.ToLower(o.StringValue()) + case "url": + url = o.StringValue() + } + } + if text == "" { + _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Please provide activity text.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + return + } + + // Mapping Mode → ActivityType + var at discordgo.ActivityType + switch mode { + case "playing": + at = discordgo.ActivityTypeGame + case "listening": + at = discordgo.ActivityTypeListening + case "watching": + at = discordgo.ActivityTypeWatching + case "streaming": + at = discordgo.ActivityTypeStreaming + case "competing": + at = discordgo.ActivityTypeCompeting + default: + at = discordgo.ActivityTypeGame + } + + // Status normalisieren + switch status { + case "online", "idle", "dnd", "invisible": + // ok + default: + status = "online" + } + + // Presence setzen + err := s.UpdateStatusComplex(discordgo.UpdateStatusData{ + Status: status, // "online" | "idle" | "dnd" | "invisible" + Activities: []*discordgo.Activity{ + { + Name: text, + Type: at, + URL: func() string { + if at == discordgo.ActivityTypeStreaming { + return url + } + return "" + }(), + }, + }, + AFK: false, + }) + if err != nil { + _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "⚠️ Failed to update presence: " + err.Error(), + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + return + } + + _ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "✅ Presence updated.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) } } } +// zusätzliches privates Kommando +var privateCommands = []*discordgo.ApplicationCommand{ + { + Name: "setpresence", + Description: "Set bot presence (owner guild only)", + DefaultMemberPermissions: &adminPerm, // nur Admins deiner Guild + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "mode", + Description: "Activity type", + Required: true, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + {Name: "playing", Value: "playing"}, + {Name: "listening", Value: "listening"}, + {Name: "watching", Value: "watching"}, + {Name: "streaming", Value: "streaming"}, + {Name: "competing", Value: "competing"}, + }, + }, + {Type: discordgo.ApplicationCommandOptionString, Name: "text", Description: "Activity text", Required: true}, + { + Type: discordgo.ApplicationCommandOptionString, Name: "status", Description: "online/idle/dnd/invisible", Required: false, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + {Name: "online", Value: "online"}, + {Name: "idle", Value: "idle"}, + {Name: "dnd", Value: "dnd"}, + {Name: "invisible", Value: "invisible"}, + }, + }, + {Type: discordgo.ApplicationCommandOptionString, Name: "url", Description: "Streaming URL (only for streaming)", Required: false}, + }, + }, +} + +func registerCommandsForGuild(s *discordgo.Session, guildID string) { + appID := s.State.User.ID + + // Basis-Commands für alle + cmds := make([]*discordgo.ApplicationCommand, 0, len(slashCommands)+len(privateCommands)) + cmds = append(cmds, slashCommands...) + + // Private Commands nur für Owner-Guild + if presenceGuildID != "" && guildID == presenceGuildID { + cmds = append(cmds, privateCommands...) + } + + // Atomar überschreiben → keine Duplikate / keine alten Schemas + created, err := s.ApplicationCommandBulkOverwrite(appID, guildID, cmds) + if err != nil { + log.Printf("BulkOverwrite %s failed: %v", guildID, err) + return + } + createdCmds.Store(guildID, created) + log.Printf("Registered %d commands in guild %s", len(created), guildID) +} + // Handler: Commands registrieren, wenn Bot auf neuen Server kommt func onGuildCreate(s *discordgo.Session, g *discordgo.GuildCreate) { - appID := s.State.User.ID - log.Printf("Registriere Commands in neuer Guild: %s (%s)", g.Name, g.ID) - for _, cmd := range slashCommands { - c, err := s.ApplicationCommandCreate(appID, g.ID, cmd) - if err != nil { - log.Printf("Command-Registrierung in %s fehlgeschlagen: %v", g.Name, err) - continue - } - - // Commands für die Guild speichern - // Wir verwenden sync.Map, also müssen wir die 'Load' und 'Store' Methoden verwenden. - commands, _ := createdCmds.LoadOrStore(g.ID, []*discordgo.ApplicationCommand{}) - commands = append(commands.([]*discordgo.ApplicationCommand), c) - createdCmds.Store(g.ID, commands) - - log.Printf("Registriere Command %s in Guild %s", cmd.Name, g.Name) - } + // Hinweis: GuildCreate feuert auch beim (Re)Connect. Genau das wollen wir hier. + log.Printf("GuildCreate: %s (%s) unavailable=%v", g.Name, g.ID, g.Unavailable) + registerCommandsForGuild(s, g.ID) } // ===== Commands Definition =====