Skip to content

Instantly share code, notes, and snippets.

@lon9
Created July 20, 2018 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lon9/42e342893ffa82ebc7688a86a12c4f11 to your computer and use it in GitHub Desktop.
Save lon9/42e342893ffa82ebc7688a86a12c4f11 to your computer and use it in GitHub Desktop.
Discord voice bot
package main
import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/bwmarrin/discordgo"
)
const (
MaxQueueSize int = 6
)
var (
queues = make(map[string][]*PlayRequest)
dg *discordgo.Session
err error
m sync.Mutex
)
type PlayRequest struct {
ChannelID string
GuildID string
Sound uint
}
func main() {
token := os.Getenv("BOT_TOKEN")
dg, err = discordgo.New("Bot " + token)
if err != nil {
panic(err)
}
dg.LogLevel = discordgo.LogDebug
dg.AddHandler(messageCreate)
err = dg.Open()
if err != nil {
panic(err)
}
fmt.Println("Airhorn is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc
dg.Close()
}
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.State.User.ID {
return
}
if strings.HasPrefix(m.Content, "!join") {
c, err := s.State.Channel(m.ChannelID)
if err != nil {
// Could not find channel.
return
}
g, err := s.State.Guild(c.GuildID)
if err != nil {
// Could not find guild.
return
}
for _, vs := range g.VoiceStates {
if vs.UserID == m.Author.ID {
requestPlaySound(g.ID, vs.ChannelID, 1)
return
}
}
}
}
func requestPlaySound(guildID, channelID string, soundID uint) {
item := &PlayRequest{
ChannelID: channelID,
GuildID: guildID,
Sound: soundID,
}
// If there is a queue setup there is alaso a player running, so just add it to the queue then
m.Lock()
if queue, ok := queues[guildID]; ok {
if len(queue) < MaxQueueSize {
queues[guildID] = append(queue, item)
}
} else {
queues[guildID] = []*PlayRequest{item}
go runPlayer(guildID)
}
m.Unlock()
}
func runPlayer(guildID string) {
var lastChannel string
var vc *discordgo.VoiceConnection
for {
m.Lock()
var item *PlayRequest
// Get the next item in the queue or quit life
if queue, ok := queues[guildID]; ok && len(queue) > 0 {
item = queue[0]
queues[guildID] = queue[1:]
} else {
break
}
m.Unlock()
// Should probably to changechannel but eh..
if lastChannel != item.ChannelID && vc != nil {
vc.Disconnect()
vc = nil
}
var err error
vc, err = playSound(vc, dg, item)
if err != nil {
log.Println(err)
}
lastChannel = item.ChannelID
}
if vc != nil {
vc.Disconnect()
}
// When we break out, playqueuemutex is locked
delete(queues, guildID)
m.Unlock()
}
func playSound(vc *discordgo.VoiceConnection, session *discordgo.Session, item *PlayRequest) (*discordgo.VoiceConnection, error) {
// Either use the passed voice connection, or create a new one
if vc == nil || !vc.Ready {
vc, err = session.ChannelVoiceJoin(item.GuildID, item.ChannelID, false, true)
if err != nil {
return nil, err
}
vc.Speaking(true)
}
// Sending voice mock
time.Sleep(time.Second)
return vc, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment