package bot import ( "log" "velox-bot/internal/commands" "velox-bot/internal/db/services" "velox-bot/internal/events" "velox-bot/internal/music" "github.com/bwmarrin/discordgo" ) type Bot struct { Session *discordgo.Session AppID string GuildID string Commands []*discordgo.ApplicationCommand registeredCommands []*discordgo.ApplicationCommand Services *services.Services LavalinkHost string LavalinkPass string } func NewBot(token, appID, guildID, lavalinkHost, lavalinkPass string, cmds []*discordgo.ApplicationCommand, services *services.Services) (*Bot, error) { session, err := discordgo.New("Bot " + token) if err != nil { return nil, err } // Intents needed for: // - InteractionCreate (slash commands): Guilds // - MessageCreate (leveling): GuildMessages // - GuildMemberAdd (welcome messages): GuildMembers // - VoiceStateUpdate (meeting lobby): GuildVoiceStates // - MessageReactionAdd (scheduling via reactions): GuildMessageReactions + DirectMessageReactions session.Identify.Intents = discordgo.IntentsGuilds | discordgo.IntentsGuildBans | discordgo.IntentsGuildMembers | discordgo.IntentsGuildMessages | discordgo.IntentsMessageContent | discordgo.IntentsGuildVoiceStates | discordgo.IntentsGuildMessageReactions | discordgo.IntentsDirectMessages | discordgo.IntentsDirectMessageReactions return &Bot{ Session: session, AppID: appID, GuildID: guildID, Commands: cmds, Services: services, LavalinkHost: lavalinkHost, LavalinkPass: lavalinkPass, }, nil } func (b *Bot) Start() error { if err := b.Session.Open(); err != nil { return err } _ = music.Init(b.Session, b.AppID, b.LavalinkHost, b.LavalinkPass) b.registeredCommands = make([]*discordgo.ApplicationCommand, len(b.Commands)) for _, cmd := range b.Commands { created, err := b.Session.ApplicationCommandCreate(b.AppID, b.GuildID, cmd) if err != nil { log.Printf("Cannot create '%v' command: %v", cmd.Name, err) continue } b.registeredCommands = append(b.registeredCommands, created) } b.Session.AddHandler(commands.HandleInteraction) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) { events.HandleMessageCreate(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.GuildMemberAdd) { events.HandleBotAddedOnGuild(s, m, b.Services) // Ignore bot members for welcome/log onboarding of normal users. if m != nil && m.Member != nil && m.User != nil && m.User.Bot { return } events.HandleGuildMemberAdd(s, m, b.Services) events.HandleGuildMemberAddLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.GuildMemberRemove) { events.HandleGuildMemberRemoveLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { events.HandleGuildMemberUpdateLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageUpdate) { events.HandleMessageUpdateLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageDelete) { events.HandleMessageDeleteLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.GuildBanAdd) { events.HandleGuildBanAddLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, m *discordgo.GuildBanRemove) { events.HandleGuildBanRemoveLog(s, m, b.Services) }) b.Session.AddHandler(func(s *discordgo.Session, vs *discordgo.VoiceStateUpdate) { events.HandleVoiceStateUpdate(s, vs, b.Services) music.OnVoiceStateUpdate(vs) }) b.Session.AddHandler(func(s *discordgo.Session, ev *discordgo.VoiceServerUpdate) { music.OnVoiceServerUpdate(ev) }) b.Session.AddHandler(func(s *discordgo.Session, r *discordgo.MessageReactionAdd) { events.HandleMessageReactionAdd(s, r, b.Services) }) events.StartScheduleReminderLoop(b.Session, b.Services) events.StartTwitchLiveLoop(b.Session, b.Services) return nil } func (b *Bot) Close() error { for _, cmd := range b.registeredCommands { if err := b.Session.ApplicationCommandDelete(b.AppID, b.GuildID, cmd.ID); err != nil { log.Printf("cannot delete '%s' command: %v", cmd.Name, err) } } return b.Session.Close() }