Skip to content

Commit

Permalink
Merge pull request bwmarrin#181 from iopred/develop
Browse files Browse the repository at this point in the history
Add a new example that acts like airhorn.solutions
  • Loading branch information
bwmarrin committed Apr 26, 2016
2 parents 48f4761 + 6225395 commit 634b61c
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
41 changes: 41 additions & 0 deletions examples/airhorn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
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 <bot token>
```

### 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 <input wav file> -raw > <output file>
```
Binary file added examples/airhorn/airhorn.dca
Binary file not shown.
186 changes: 186 additions & 0 deletions examples/airhorn/main.go
Original file line number Diff line number Diff line change
@@ -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 <bot token>")
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
}
1 change: 1 addition & 0 deletions examples/new_basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 634b61c

Please sign in to comment.