diff --git a/README.md b/README.md index 5cd645e..21533c6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ DiscordGo ==== -[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)][https://discord.gg/0f1SbxBZjYoCtNPP] [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)][https://discord.gg/0SBTUU1wZTWT6sqd] +[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) +[![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.gg/0SBTUU1wZTWT6sqd) DiscordGo is a [Go](https://golang.org/) package that provides low level bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo @@ -15,7 +16,7 @@ with additional voice helper functions and features. tool that wraps `ffmpeg` to create opus encoded audio appropriate for use with Discord (and DiscordGo) -Join [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)][https://discord.gg/0f1SbxBZjYoCtNPP] +Join [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) Discord chat channel for support. ## Getting Started diff --git a/examples/airhorn/README.md b/examples/airhorn/README.md new file mode 100644 index 0000000..e9e49e1 --- /dev/null +++ b/examples/airhorn/README.md @@ -0,0 +1,41 @@ + +Airhorn Example +==== + +This example demonstrates how to utilize DiscordGo to listen to an !airhorn +command in a channel and play a sound to that users current voice channel. + +### Build + +This assumes you already have a working Go environment setup and that +DiscordGo is correctly installed on your system. + +```sh +go install github.com/bwmarrin/discordgo/examples/airhorn +cd $GOPATH/bin +cp ../src/github.com/bwmarrin/discordgo/examples/airhorn/airhorn.dca . +``` + +### Usage + +``` +Usage of ./airhorn: + -t string + Account Token +``` + +The below example shows how to start the bot. + +```sh +./airhorn -t +``` + +### Creating sounds + +Airhorn bot uses DCA files that are pre-computed files that are easy to send to Discord. + +If you would like to create your own DCA files, please use [https://github.com/bwmarrin/dca/tree/master/cmd/dca](DCA). + +```sh +./dca -i -raw > +``` diff --git a/examples/airhorn/airhorn.dca b/examples/airhorn/airhorn.dca new file mode 100644 index 0000000..39e7167 Binary files /dev/null and b/examples/airhorn/airhorn.dca differ diff --git a/examples/airhorn/main.go b/examples/airhorn/main.go new file mode 100644 index 0000000..ba0312e --- /dev/null +++ b/examples/airhorn/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "encoding/binary" + "flag" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/bwmarrin/discordgo" +) + +func init() { + flag.StringVar(&token, "t", "", "Account Token") + flag.Parse() +} + +var token string +var buffer = make([][]byte, 0) + +func main() { + if token == "" { + fmt.Println("No token provided. Please run: airhorn -t ") + return + } + + // Load the sound file. + err := loadSound() + if err != nil { + fmt.Println("Error loading sound: ", err) + fmt.Println("Please copy $GOPATH/src/github.com/bwmarrin/examples/airhorn/airhorn.dca to this directory.") + return + } + + // Create a new Discord session using the provided token. + dg, err := discordgo.New(token) + if err != nil { + fmt.Println("Error creating Discord session: ", err) + return + } + + // Register ready as a callback for the ready events. + dg.AddHandler(ready) + + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) + + // Register guildCreate as a callback for the guildCreate events. + dg.AddHandler(guildCreate) + + // Open the websocket and begin listening. + err = dg.Open() + if err != nil { + fmt.Println("Error opening Discord session: ", err) + } + + fmt.Println("Airhorn is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +func ready(s *discordgo.Session, event *discordgo.Ready) { + // Set the playing status. + s.UpdateStatus(0, "!airhorn") +} + +// This function will be called (due to AddHandler above) every time a new +// message is created on any channel that the autenticated bot has access to. +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + if strings.HasPrefix(m.Content, "!airhorn") { + // Find the channel that the message came from. + c, err := s.State.Channel(m.ChannelID) + if err != nil { + // Could not find channel. + return + } + + // Find the guild for that channel. + g, err := s.State.Guild(c.GuildID) + if err != nil { + // Could not find guild. + return + } + + // Look for the message sender in that guilds current voice states. + for _, vs := range g.VoiceStates { + if vs.UserID == m.Author.ID { + err = playSound(s, g.ID, vs.ChannelID) + if err != nil { + fmt.Println("Error playing sound:", err) + } + + return + } + } + } +} + +// This function will be called (due to AddHandler above) every time a new +// guild is joined. +func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { + if event.Guild.Unavailable != nil { + return + } + + for _, channel := range event.Guild.Channels { + if channel.ID == event.Guild.ID { + s.ChannelMessageSend(channel.ID, "Airhorn is ready! Type !airhorn while in a voice channel to play a sound.") + return + } + } +} + +// loadSound attempts to load an encoded sound file from disk. +func loadSound() error { + file, err := os.Open("airhorn.dca") + + if err != nil { + fmt.Println("Error opening dca file :", err) + return err + } + + var opuslen int16 + + for { + // Read opus frame length from dca file. + err = binary.Read(file, binary.LittleEndian, &opuslen) + + // If this is the end of the file, just return. + if err == io.EOF || err == io.ErrUnexpectedEOF { + return nil + } + + if err != nil { + fmt.Println("Error reading from dca file :", err) + return err + } + + // Read encoded pcm from dca file. + InBuf := make([]byte, opuslen) + err = binary.Read(file, binary.LittleEndian, &InBuf) + + // Should not be any end of file errors + if err != nil { + fmt.Println("Error reading from dca file :", err) + return err + } + + // Append encoded pcm data to the buffer. + buffer = append(buffer, InBuf) + } +} + +// playSound plays the current buffer to the provided channel. +func playSound(s *discordgo.Session, guildID, channelID string) (err error) { + // Join the provided voice channel. + vc, err := s.ChannelVoiceJoin(guildID, channelID, false, false) + if err != nil { + return err + } + + // Sleep for a specified amount of time before playing the sound + time.Sleep(250 * time.Millisecond) + + // Start speaking. + vc.Speaking(true) + + // Send the buffer data. + for _, buff := range buffer { + vc.OpusSend <- buff + } + + // Stop speaking + vc.Speaking(false) + + // Sleep for a specificed amount of time before ending. + time.Sleep(250 * time.Millisecond) + + // Disconnect from the provided voice channel. + vc.Disconnect() + + return nil +} diff --git a/examples/new_basic/main.go b/examples/new_basic/main.go index 3d2fb97..7b2dc73 100644 --- a/examples/new_basic/main.go +++ b/examples/new_basic/main.go @@ -26,6 +26,7 @@ func init() { func main() { // Create a new Discord session using the provided login information. + // Use discordgo.New(Token) to just use a token for login. dg, err := discordgo.New(Email, Password, Token) if err != nil { fmt.Println("error creating Discord session,", err)