diff --git a/main.go b/main.go index 28bd7ed..973e9a4 100644 --- a/main.go +++ b/main.go @@ -4,46 +4,31 @@ import ( "fmt" "log" "os" - "os/exec" "os/signal" "syscall" "time" + "git.thegrind.dev/thegrind/papibot/pkg/bot" "git.thegrind.dev/thegrind/papibot/pkg/opusframes" + "git.thegrind.dev/thegrind/papibot/pkg/youtube" dg "github.com/bwmarrin/discordgo" "github.com/javif89/dotenv" ) -var commandHandlers = map[string]func(s *dg.Session, i *dg.InteractionCreate){ - "play": playCommand, -} func main() { env := dotenv.Load(".env") botToken := env.Get("DISCORD_BOT_TOKEN") + appId := env.Get("DISCORD_APP_ID") + + bot := bot.New(botToken, appId) log.Println("Starting bot") + err := bot.Start() + log.Println("Error starting the bot", err) - var discord *dg.Session - var err error - - discord, err = dg.New(fmt.Sprintf("Bot %s", botToken)) - - if err != nil { - log.Println("Error starting the bot", err) - return - } - - discord.AddHandler(ready) - - discord.Identify.Intents = dg.IntentsGuilds | dg.IntentsGuildMessages | dg.IntentsGuildVoiceStates - - err = discord.Open() - if err != nil { - log.Println("Error opening Discord session: ", err) - } - - // Register slash commands - discord.AddHandler(handleSlashCommand) + log.Println("Adding command handlers") + bot.AddSlashCommand("play", playCommand) + log.Println("Done") log.Println("Bot is running. Press CTRL+C to exit.") sc := make(chan os.Signal, 1) @@ -51,15 +36,7 @@ func main() { <-sc log.Println("Bot is exiting...") - log.Println("Removing commands") - - appId := env.Get("DISCORD_APP_ID") - cmds, _ := discord.ApplicationCommands(appId, "338782945110392832") - for _, cmd := range cmds { - discord.ApplicationCommandDelete(appId, "338782945110392832", cmd.ID) - } - - discord.Close() + bot.Stop() log.Println("Goodbye!") } @@ -75,7 +52,12 @@ func playCommand(s *dg.Session, i *dg.InteractionCreate) { }, }) - downloadVideo(url) + os.Remove("vid.webm") + youtube.Download(url, "vid.webm") + start := time.Now() + opusframes.Encode("vid.webm", "vid.of") + duration := time.Since(start) + log.Printf("Encoding took: %s", duration) // Get the channel msgServer, err := s.State.Guild(i.GuildID) @@ -130,62 +112,4 @@ func playOnVoiceChannel(voiceChannel *dg.VoiceConnection) { log.Printf("Sending packet: %d bytes", len(p)) voiceChannel.OpusSend <- p } -} - -func handleSlashCommand(s *dg.Session, i *dg.InteractionCreate) { - log.Println("Command received?") - if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok { - h(s, i) - } -} - -func ready(s *dg.Session, event *dg.Ready) { - commands := []*dg.ApplicationCommand{ - { - Name: "play", - Description: "Play a song from youtube, spotify, apple music, etc", - Options: []*dg.ApplicationCommandOption{ - { - Type: dg.ApplicationCommandOptionString, - Name: "url", - Description: "URL to the song", - Required: true, - }, - }, - }, - } - - env := dotenv.Load(".env") - appId := env.Get("DISCORD_APP_ID") - - log.Println("Bot is ready") - log.Println("Bulk registering commands") - _, err := s.ApplicationCommandBulkOverwrite(appId, "338782945110392832", commands) - if err != nil { - log.Fatal(err) - } - log.Println("Done") -} - -func downloadVideo(url string) error { - log.Printf("Downloading: %s", url) - os.Remove("vid.webm") - cmd := exec.Command( - "yt-dlp", - "-i", - "-q", - "-f", "bestaudio", - "-o", - "vid.%(ext)s", - url) - - err := cmd.Run() - log.Println("Downloaded") - - start := time.Now() - opusframes.Encode("vid.webm", "vid.of") - duration := time.Since(start) - log.Printf("Encoding took: %s", duration) - - return err } \ No newline at end of file diff --git a/pkg/bot/bot.go b/pkg/bot/bot.go new file mode 100644 index 0000000..d3c02fb --- /dev/null +++ b/pkg/bot/bot.go @@ -0,0 +1,97 @@ +package bot + +import ( + "fmt" + "log" + dg "github.com/bwmarrin/discordgo" +) + +type Bot struct { + token string + appId string + isPlaying bool + session *dg.Session + slashCommands map[string]func(s *dg.Session, i *dg.InteractionCreate) +} + +func New(token, appId string) *Bot { + return &Bot{ + token: token, + appId: appId, + slashCommands: make(map[string]func(s *dg.Session, i *dg.InteractionCreate)), + } +} + +// TODO: Let's get some custom error types going +// at some point +func (b *Bot) Start() error { + var err error + + b.session, err = dg.New(fmt.Sprintf("Bot %s", b.token)) + + if err != nil { + return err + } + + b.session.AddHandler(b.ready) + + b.session.Identify.Intents = dg.IntentsGuilds | dg.IntentsGuildMessages | dg.IntentsGuildVoiceStates + + err = b.session.Open() + if err != nil { + return err + } + + // Register slash commands + b.session.AddHandler(b.handleSlashCommand) + + return nil +} + +// TODO: Don't hard code the server. We can +// register the commands globally +func (b *Bot) Stop() { + cmds, _ := b.session.ApplicationCommands(b.appId, "338782945110392832") + for _, cmd := range cmds { + b.session.ApplicationCommandDelete(b.appId, "338782945110392832", cmd.ID) + } + + b.session.Close() +} + +func (b *Bot) AddSlashCommand(name string, handler func(*dg.Session, *dg.InteractionCreate)) { + b.slashCommands[name] = handler +} + + +func (b *Bot) handleSlashCommand(s *dg.Session, i *dg.InteractionCreate) { + log.Println("Command received?") + if h, ok := b.slashCommands[i.ApplicationCommandData().Name]; ok { + h(s, i) + } +} + +func (b *Bot) ready(s *dg.Session, event *dg.Ready) { + commands := []*dg.ApplicationCommand{ + { + Name: "play", + Description: "Play a song from youtube, spotify, apple music, etc", + Options: []*dg.ApplicationCommandOption{ + { + Type: dg.ApplicationCommandOptionString, + Name: "url", + Description: "URL to the song", + Required: true, + }, + }, + }, + } + + log.Println("Bot is ready") + log.Println("Bulk registering commands") + _, err := s.ApplicationCommandBulkOverwrite(b.appId, "338782945110392832", commands) + if err != nil { + log.Fatal(err) + } + log.Println("Done") +} diff --git a/pkg/youtube/youtube.go b/pkg/youtube/youtube.go new file mode 100644 index 0000000..7a1956a --- /dev/null +++ b/pkg/youtube/youtube.go @@ -0,0 +1,24 @@ +package youtube + +import ( + "os/exec" +) + +func Download(url, outPath string) error { + cmd := exec.Command( + "yt-dlp", + "-i", + "-q", + "-f", "bestaudio", + "-o", + outPath, + url) + + err := cmd.Run() + + if err != nil { + return err + } + + return err +} \ No newline at end of file