Merge branch 'main' of github.com:stryan/mumble-discord-bridge

This commit is contained in:
stryan 2021-04-15 11:10:20 -04:00
commit 812733d669
12 changed files with 180 additions and 53 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ mumble-discord-bridge
dist dist
bridge bridge
.prof .prof
cert.pem

View File

@ -6,6 +6,9 @@ mumble-discord-bridge: $(GOFILES)
dev: $(GOFILES) dev: $(GOFILES)
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge
dev-race: $(GOFILES)
go run -race *.go
dev-profile: $(GOFILES) dev-profile: $(GOFILES)
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof

View File

@ -27,11 +27,13 @@ Usage of ./mumble-discord-bridge:
-mumble-address string -mumble-address string
MUMBLE_ADDRESS, mumble server address, example example.com, required MUMBLE_ADDRESS, mumble server address, example example.com, required
-mumble-channel string -mumble-channel string
MUMBLE_CHANNEL, mumble channel to start in, optional MUMBLE_CHANNEL, mumble channel to start in, using '/' to separate nested channels, optional
-mumble-disable-text -mumble-disable-text
MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false) MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false)
-mumble-insecure -mumble-insecure
MUMBLE_INSECURE, mumble insecure, optional MUMBLE_INSECURE, mumble insecure, optional
-mumble-certificate
MUMBLE_CERTIFICATE, mumble client certificate, optional
-mumble-password string -mumble-password string
MUMBLE_PASSWORD, mumble password, optional MUMBLE_PASSWORD, mumble password, optional
-mumble-port int -mumble-port int
@ -40,6 +42,8 @@ Usage of ./mumble-discord-bridge:
MUMBLE_USERNAME, mumble username, (default: discord) (default "Discord") MUMBLE_USERNAME, mumble username, (default: discord) (default "Discord")
-nice -nice
NICE, whether the bridge should automatically try to 'nice' itself, (default false) NICE, whether the bridge should automatically try to 'nice' itself, (default false)
-debug
DEBUG_LEVEL, DISCORD debug level, optional (default: 1)
``` ```
The bridge can be run with the follow modes: The bridge can be run with the follow modes:
@ -74,7 +78,31 @@ The guide below provides information on how to setup a Discord bot.
[Create a Discord Bot](https://discordpy.readthedocs.io/en/latest/discord.html) [Create a Discord Bot](https://discordpy.readthedocs.io/en/latest/discord.html)
Individual Discord servers need to invite the bot before it can connect. Individual Discord servers need to invite the bot before it can connect.
The bot requires the following permissions:
* View Channels
* See Messages
* Read Message History
* Voice Channel Connect
* Voice Channel Speak
* Voice Channel Use Voice Activity
### Finding Discord CID and GID
Discord GID is a unique ID linked to one Discord Server, also called Guild. CID is similarly a unique ID for a Discord Channel. To find these you need to set Discord into developer Mode.
[Instructions to enable Discord Developer Mode](https://discordia.me/en/developer-mode)
Then you can get the GID by right-clicking your server and selecting Copy-ID. Similarly the CID can be found right clicking the voice channel and selecting Copy ID.
### Generating Mumble Client (Optional)
Optionally you can specify a client certificate for mumble [Mumble Certificates](https://wiki.mumble.info/wiki/Mumble_Certificates)
If you don't have a client certificate, you can generate one with this command:
``` bash
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout cert.pem -out cert.pem -subj "/CN=mumble-discord-bridge"
```
### Binary ### Binary
@ -141,6 +169,9 @@ Audio leveling from Discord needs to be improved.
Delays in connecting to Mumble (such as from external authentication plugins) may result in extra error messages on initial connection. Delays in connecting to Mumble (such as from external authentication plugins) may result in extra error messages on initial connection.
There is an issue seen with Mumble-Server (murmur) 1.3.0 in which the bridge will loose the ability to send messages client after prolonged periods of connectivity.
This issue has been appears to be resolved by murmur 1.3.4.
## License ## License
Distributed under the MIT License. See LICENSE for more information. Distributed under the MIT License. See LICENSE for more information.

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"os"
"strconv" "strconv"
"sync" "sync"
"time" "time"
@ -34,6 +35,9 @@ type BridgeState struct {
// Wait for bridge to exit cleanly // Wait for bridge to exit cleanly
WaitExit *sync.WaitGroup WaitExit *sync.WaitGroup
// Bridge State Mutex
BridgeMutex sync.Mutex
// Bridge connection // Bridge connection
Connected bool Connected bool
@ -70,6 +74,9 @@ type BridgeState struct {
// Mumble Duplex and Event Listener // Mumble Duplex and Event Listener
MumbleStream *MumbleDuplex MumbleStream *MumbleDuplex
MumbleListener *MumbleListener MumbleListener *MumbleListener
// Discord Voice channel to join
DiscordChannelID string
} }
// startBridge established the voice connection // startBridge established the voice connection
@ -90,7 +97,12 @@ func (b *BridgeState) startBridge() {
// DISCORD Connect Voice // DISCORD Connect Voice
log.Println("Attempting to join Discord voice channel") log.Println("Attempting to join Discord voice channel")
b.DiscordVoice, err = b.DiscordSession.ChannelVoiceJoin(b.BridgeConfig.GID, b.BridgeConfig.CID, false, false) if b.DiscordChannelID == "" {
log.Println("Tried to start bridge but no Discord channel specified")
return
}
b.DiscordVoice, err = b.DiscordSession.ChannelVoiceJoin(b.BridgeConfig.GID, b.DiscordChannelID, false, false)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
b.DiscordVoice.Disconnect() b.DiscordVoice.Disconnect()
@ -111,6 +123,16 @@ func (b *BridgeState) startBridge() {
tlsConfig.InsecureSkipVerify = true tlsConfig.InsecureSkipVerify = true
} }
if b.BridgeConfig.MumbleCertificate != "" {
keyFile := b.BridgeConfig.MumbleCertificate
if certificate, err := tls.LoadX509KeyPair(keyFile, keyFile); err != nil {
fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err)
os.Exit(1)
} else {
tlsConfig.Certificates = append(tlsConfig.Certificates, certificate)
}
}
log.Println("Attempting to join Mumble") log.Println("Attempting to join Mumble")
b.MumbleClient, err = gumble.DialWithDialer(new(net.Dialer), b.BridgeConfig.MumbleAddr, b.BridgeConfig.MumbleConfig, &tlsConfig) b.MumbleClient, err = gumble.DialWithDialer(new(net.Dialer), b.BridgeConfig.MumbleAddr, b.BridgeConfig.MumbleConfig, &tlsConfig)
@ -149,6 +171,7 @@ func (b *BridgeState) startBridge() {
go b.DiscordStream.discordSendPCM(ctx, &wg, cancel, toDiscord) go b.DiscordStream.discordSendPCM(ctx, &wg, cancel, toDiscord)
// Monitor Mumble // Monitor Mumble
wg.Add(1)
go func() { go func() {
wg.Add(1) wg.Add(1)
ticker := time.NewTicker(500 * time.Millisecond) ticker := time.NewTicker(500 * time.Millisecond)
@ -170,7 +193,9 @@ func (b *BridgeState) startBridge() {
} }
}() }()
b.BridgeMutex.Lock()
b.Connected = true b.Connected = true
b.BridgeMutex.Unlock()
// Hold until cancelled or external die request // Hold until cancelled or external die request
select { select {
@ -181,7 +206,10 @@ func (b *BridgeState) startBridge() {
cancel() cancel()
} }
b.BridgeMutex.Lock()
b.Connected = false b.Connected = false
b.BridgeMutex.Unlock()
wg.Wait() wg.Wait()
log.Println("Terminating Bridge") log.Println("Terminating Bridge")
b.MumbleUsersMutex.Lock() b.MumbleUsersMutex.Lock()
@ -202,6 +230,7 @@ func (b *BridgeState) discordStatusUpdate() {
b.DiscordSession.UpdateListeningStatus("an error pinging mumble") b.DiscordSession.UpdateListeningStatus("an error pinging mumble")
} else { } else {
b.MumbleUsersMutex.Lock() b.MumbleUsersMutex.Lock()
b.BridgeMutex.Lock()
b.MumbleUserCount = resp.ConnectedUsers b.MumbleUserCount = resp.ConnectedUsers
if b.Connected { if b.Connected {
b.MumbleUserCount = b.MumbleUserCount - 1 b.MumbleUserCount = b.MumbleUserCount - 1
@ -215,6 +244,7 @@ func (b *BridgeState) discordStatusUpdate() {
status = fmt.Sprintf("%v users in Mumble\n", b.MumbleUserCount) status = fmt.Sprintf("%v users in Mumble\n", b.MumbleUserCount)
} }
} }
b.BridgeMutex.Unlock()
b.MumbleUsersMutex.Unlock() b.MumbleUsersMutex.Unlock()
b.DiscordSession.UpdateListeningStatus(status) b.DiscordSession.UpdateListeningStatus(status)
} }
@ -238,6 +268,7 @@ func (b *BridgeState) AutoBridge() {
b.MumbleUsersMutex.Lock() b.MumbleUsersMutex.Lock()
b.DiscordUsersMutex.Lock() b.DiscordUsersMutex.Lock()
b.BridgeMutex.Lock()
if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 { if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 {
log.Println("users detected in mumble and discord, bridging") log.Println("users detected in mumble and discord, bridging")
@ -248,6 +279,7 @@ func (b *BridgeState) AutoBridge() {
b.BridgeDie <- true b.BridgeDie <- true
} }
b.BridgeMutex.Unlock()
b.MumbleUsersMutex.Unlock() b.MumbleUsersMutex.Unlock()
b.DiscordUsersMutex.Unlock() b.DiscordUsersMutex.Unlock()
} }

View File

@ -24,7 +24,8 @@ type BridgeConfig struct {
MumbleConfig *gumble.Config MumbleConfig *gumble.Config
MumbleAddr string MumbleAddr string
MumbleInsecure bool MumbleInsecure bool
MumbleChannel string MumbleCertificate string
MumbleChannel []string
MumbleDisableText bool MumbleDisableText bool
Command string Command string
GID string GID string

View File

@ -24,7 +24,7 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
} }
for _, vs := range event.VoiceStates { for _, vs := range event.VoiceStates {
if vs.ChannelID == l.Bridge.BridgeConfig.CID { if vs.ChannelID == l.Bridge.DiscordChannelID {
if s.State.User.ID == vs.UserID { if s.State.User.ID == vs.UserID {
// Ignore bot // Ignore bot
continue continue
@ -49,11 +49,13 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
l.Bridge.DiscordUsersMutex.Unlock() l.Bridge.DiscordUsersMutex.Unlock()
// If connected to mumble inform users of Discord users // If connected to mumble inform users of Discord users
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText { if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() { l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false) l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false)
}) })
} }
l.Bridge.BridgeMutex.Unlock()
} }
} }
@ -61,10 +63,6 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
if l.Bridge.Mode == bridgeModeConstant {
return
}
// Ignore all messages created by the bot itself // Ignore all messages created by the bot itself
if m.Author.ID == s.State.User.ID { if m.Author.ID == s.State.User.ID {
return return
@ -83,11 +81,26 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
return return
} }
prefix := "!" + l.Bridge.BridgeConfig.Command prefix := "!" + l.Bridge.BridgeConfig.Command
if l.Bridge.Mode == bridgeModeConstant && strings.HasPrefix(m.Content, prefix) {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Constant mode enabled, manual commands can not be entered")
return
}
l.Bridge.BridgeMutex.Lock()
bridgeConnected := l.Bridge.Connected
l.Bridge.BridgeMutex.Unlock()
if strings.HasPrefix(m.Content, prefix+" link") { if strings.HasPrefix(m.Content, prefix+" link") {
// Look for the message sender in that guild's current voice states. // Look for the message sender in that guild's current voice states.
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge already running, unlink first")
return
}
if vs.UserID == m.Author.ID { if vs.UserID == m.Author.ID {
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
l.Bridge.DiscordChannelID = vs.ChannelID
go l.Bridge.startBridge() go l.Bridge.startBridge()
return return
} }
@ -96,8 +109,12 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if strings.HasPrefix(m.Content, prefix+" unlink") { if strings.HasPrefix(m.Content, prefix+" unlink") {
// Look for the message sender in that guild's current voice states. // Look for the message sender in that guild's current voice states.
if !bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge is not currently running")
return
}
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if vs.UserID == m.Author.ID { if vs.UserID == m.Author.ID && vs.ChannelID == l.Bridge.DiscordChannelID {
log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID)
l.Bridge.BridgeDie <- true l.Bridge.BridgeDie <- true
return return
@ -107,6 +124,10 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if strings.HasPrefix(m.Content, prefix+" refresh") { if strings.HasPrefix(m.Content, prefix+" refresh") {
// Look for the message sender in that guild's current voice states. // Look for the message sender in that guild's current voice states.
if !bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge is not currently running")
return
}
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if vs.UserID == m.Author.ID { if vs.UserID == m.Author.ID {
log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID)
@ -122,10 +143,14 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if strings.HasPrefix(m.Content, prefix+" auto") { if strings.HasPrefix(m.Content, prefix+" auto") {
if l.Bridge.Mode != bridgeModeAuto { if l.Bridge.Mode != bridgeModeAuto {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode enabled")
l.Bridge.Mode = bridgeModeAuto l.Bridge.Mode = bridgeModeAuto
l.Bridge.DiscordChannelID = l.Bridge.BridgeConfig.CID
l.Bridge.AutoChanDie = make(chan bool) l.Bridge.AutoChanDie = make(chan bool)
go l.Bridge.AutoBridge() go l.Bridge.AutoBridge()
} else { } else {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode disabled")
l.Bridge.DiscordChannelID = ""
l.Bridge.AutoChanDie <- true l.Bridge.AutoChanDie <- true
l.Bridge.Mode = bridgeModeManual l.Bridge.Mode = bridgeModeManual
} }
@ -152,7 +177,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
// Sync the channel voice states to the local discordUsersMap // Sync the channel voice states to the local discordUsersMap
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if vs.ChannelID == l.Bridge.BridgeConfig.CID { if vs.ChannelID == l.Bridge.DiscordChannelID {
if s.State.User.ID == vs.UserID { if s.State.User.ID == vs.UserID {
// Ignore bot // Ignore bot
continue continue
@ -176,11 +201,13 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
seen: true, seen: true,
dm: dm, dm: dm,
} }
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText { if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() { l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false) l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false)
}) })
} }
l.Bridge.BridgeMutex.Unlock()
} else { } else {
du := l.Bridge.DiscordUsers[vs.UserID] du := l.Bridge.DiscordUsers[vs.UserID]
du.seen = true du.seen = true
@ -192,13 +219,15 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
// Remove users that are no longer connected // Remove users that are no longer connected
for id := range l.Bridge.DiscordUsers { for id := range l.Bridge.DiscordUsers {
if l.Bridge.DiscordUsers[id].seen == false { if !l.Bridge.DiscordUsers[id].seen {
log.Println("User left Discord channel " + l.Bridge.DiscordUsers[id].username) log.Println("User left Discord channel " + l.Bridge.DiscordUsers[id].username)
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText { if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() { l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", l.Bridge.DiscordUsers[id].username), false) l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", l.Bridge.DiscordUsers[id].username), false)
}) })
} }
l.Bridge.BridgeMutex.Unlock()
delete(l.Bridge.DiscordUsers, id) delete(l.Bridge.DiscordUsers, id)
} }
} }

View File

@ -23,9 +23,8 @@ type fromDiscord struct {
type DiscordDuplex struct { type DiscordDuplex struct {
Bridge *BridgeState Bridge *BridgeState
discordMutex sync.Mutex discordMutex sync.Mutex
discordMixerMutex sync.Mutex fromDiscordMap map[uint32]fromDiscord
fromDiscordMap map[uint32]fromDiscord
} }
// OnError gets called by dgvoice when an error is encountered. // OnError gets called by dgvoice when an error is encountered.
@ -87,8 +86,9 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
continue continue
} }
if dd.Bridge.DiscordVoice.Ready == false || dd.Bridge.DiscordVoice.OpusSend == nil { dd.Bridge.DiscordVoice.RWMutex.RLock()
if lastReady == true { if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusSend == nil {
if lastReady {
OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil) OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
readyTimeout = time.AfterFunc(30*time.Second, func() { readyTimeout = time.AfterFunc(30*time.Second, func() {
log.Println("set ready timeout") log.Println("set ready timeout")
@ -96,13 +96,15 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
}) })
lastReady = false lastReady = false
} }
continue } else if !lastReady {
} else if lastReady == false {
fmt.Println("Discordgo ready to send opus packets") fmt.Println("Discordgo ready to send opus packets")
lastReady = true lastReady = true
readyTimeout.Stop() readyTimeout.Stop()
} else {
dd.Bridge.DiscordVoice.OpusSend <- opus
} }
dd.Bridge.DiscordVoice.OpusSend <- opus dd.Bridge.DiscordVoice.RWMutex.RUnlock()
} else { } else {
if streaming { if streaming {
dd.Bridge.DiscordVoice.Speaking(false) dd.Bridge.DiscordVoice.Speaking(false)
@ -123,8 +125,9 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
wg.Add(1) wg.Add(1)
for { for {
if dd.Bridge.DiscordVoice.Ready == false || dd.Bridge.DiscordVoice.OpusRecv == nil { dd.Bridge.DiscordVoice.RWMutex.RLock()
if lastReady == true { if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusRecv == nil {
if lastReady {
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil) OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
readyTimeout = time.AfterFunc(30*time.Second, func() { readyTimeout = time.AfterFunc(30*time.Second, func() {
log.Println("set ready timeout") log.Println("set ready timeout")
@ -133,11 +136,12 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
lastReady = false lastReady = false
} }
continue continue
} else if lastReady == false { } else if !lastReady {
fmt.Println("Discordgo ready to receive packets") fmt.Println("Discordgo ready to receive packets")
lastReady = true lastReady = true
readyTimeout.Stop() readyTimeout.Stop()
} }
dd.Bridge.DiscordVoice.RWMutex.RUnlock()
var ok bool var ok bool
var p *discordgo.Packet var p *discordgo.Packet
@ -222,7 +226,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
for i := range dd.fromDiscordMap { for i := range dd.fromDiscordMap {
if len(dd.fromDiscordMap[i].pcm) > 0 { if len(dd.fromDiscordMap[i].pcm) > 0 {
sendAudio = true sendAudio = true
if dd.fromDiscordMap[i].streaming == false { if !dd.fromDiscordMap[i].streaming {
x := dd.fromDiscordMap[i] x := dd.fromDiscordMap[i]
x.streaming = true x.streaming = true
dd.fromDiscordMap[i] = x dd.fromDiscordMap[i] = x
@ -231,7 +235,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
x1 := (<-dd.fromDiscordMap[i].pcm) x1 := (<-dd.fromDiscordMap[i].pcm)
internalMixerArr = append(internalMixerArr, x1) internalMixerArr = append(internalMixerArr, x1)
} else { } else {
if dd.fromDiscordMap[i].streaming == true { if dd.fromDiscordMap[i].streaming {
x := dd.fromDiscordMap[i] x := dd.fromDiscordMap[i]
x.streaming = false x.streaming = false
dd.fromDiscordMap[i] = x dd.fromDiscordMap[i] = x

2
go.mod
View File

@ -3,7 +3,7 @@ module git.saintnet.tech/stryan/yammerbot
go 1.15 go 1.15
require ( require (
github.com/bwmarrin/discordgo v0.22.0 github.com/bwmarrin/discordgo v0.23.2
github.com/golang/protobuf v1.4.3 // indirect github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/joho/godotenv v1.3.0 github.com/joho/godotenv v1.3.0

23
go.sum
View File

@ -1,13 +1,23 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM= github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM=
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI= github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -30,42 +40,54 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -78,6 +100,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa h1:WNU4LYsgD2UHxgKgB36mL6iMAMOvr127alafSlgBbiA= layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa h1:WNU4LYsgD2UHxgKgB36mL6iMAMOvr127alafSlgBbiA=
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa/go.mod h1:AOef7vHz0+v4sWwJnr0jSyHiX/1NgsMoaxl+rEPz/I0= layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa/go.mod h1:AOef7vHz0+v4sWwJnr0jSyHiX/1NgsMoaxl+rEPz/I0=

19
main.go
View File

@ -8,6 +8,7 @@ import (
"os/signal" "os/signal"
"runtime/pprof" "runtime/pprof"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
@ -38,7 +39,8 @@ func main() {
mumbleUsername := flag.String("mumble-username", lookupEnvOrString("MUMBLE_USERNAME", "Discord"), "MUMBLE_USERNAME, mumble username, (default: discord)") mumbleUsername := flag.String("mumble-username", lookupEnvOrString("MUMBLE_USERNAME", "Discord"), "MUMBLE_USERNAME, mumble username, (default: discord)")
mumblePassword := flag.String("mumble-password", lookupEnvOrString("MUMBLE_PASSWORD", ""), "MUMBLE_PASSWORD, mumble password, optional") mumblePassword := flag.String("mumble-password", lookupEnvOrString("MUMBLE_PASSWORD", ""), "MUMBLE_PASSWORD, mumble password, optional")
mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), " MUMBLE_INSECURE, mumble insecure, optional") mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), " MUMBLE_INSECURE, mumble insecure, optional")
mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "MUMBLE_CHANNEL, mumble channel to start in, optional") mumbleCertificate := flag.String("mumble-certificate", lookupEnvOrString("MUMBLE_CERTIFICATE", ""), "MUMBLE_CERTIFICATE, client certificate to use when connecting to the Mumble server")
mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "MUMBLE_CHANNEL, mumble channel to start in, using '/' to separate nested channels, optional")
mumbleDisableText := flag.Bool("mumble-disable-text", lookupEnvOrBool("MUMBLE_DISABLE_TEXT", false), "MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false)") mumbleDisableText := flag.Bool("mumble-disable-text", lookupEnvOrBool("MUMBLE_DISABLE_TEXT", false), "MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false)")
discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token, required") discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token, required")
discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid, required") discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid, required")
@ -47,6 +49,7 @@ func main() {
discordDisableText := flag.Bool("discord-disable-text", lookupEnvOrBool("DISCORD_DISABLE_TEXT", false), "DISCORD_DISABLE_TEXT, disable sending direct messages to discord, (default false)") discordDisableText := flag.Bool("discord-disable-text", lookupEnvOrBool("DISCORD_DISABLE_TEXT", false), "DISCORD_DISABLE_TEXT, disable sending direct messages to discord, (default false)")
mode := flag.String("mode", lookupEnvOrString("MODE", "constant"), "MODE, [constant, manual, auto] determine which mode the bridge starts in, (default constant)") mode := flag.String("mode", lookupEnvOrString("MODE", "constant"), "MODE, [constant, manual, auto] determine which mode the bridge starts in, (default constant)")
nice := flag.Bool("nice", lookupEnvOrBool("NICE", false), "NICE, whether the bridge should automatically try to 'nice' itself, (default false)") nice := flag.Bool("nice", lookupEnvOrBool("NICE", false), "NICE, whether the bridge should automatically try to 'nice' itself, (default false)")
debug := flag.Int("debug-level", lookupEnvOrInt("DEBUG", 1), "DEBUG_LEVEL, Discord debug level, optional, (default 1)")
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`") cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`")
@ -99,7 +102,8 @@ func main() {
// MumbleConfig: config, // MumbleConfig: config,
MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort),
MumbleInsecure: *mumbleInsecure, MumbleInsecure: *mumbleInsecure,
MumbleChannel: *mumbleChannel, MumbleCertificate: *mumbleCertificate,
MumbleChannel: strings.Split(*mumbleChannel, "/"),
MumbleDisableText: *mumbleDisableText, MumbleDisableText: *mumbleDisableText,
Command: *discordCommand, Command: *discordCommand,
GID: *discordGID, GID: *discordGID,
@ -135,7 +139,7 @@ func main() {
return return
} }
Bridge.DiscordSession.LogLevel = 1 Bridge.DiscordSession.LogLevel = *debug
Bridge.DiscordSession.StateEnabled = true Bridge.DiscordSession.StateEnabled = true
Bridge.DiscordSession.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) Bridge.DiscordSession.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged)
Bridge.DiscordSession.ShouldReconnectOnError = true Bridge.DiscordSession.ShouldReconnectOnError = true
@ -163,6 +167,7 @@ func main() {
log.Println("bridge starting in automatic mode") log.Println("bridge starting in automatic mode")
Bridge.AutoChanDie = make(chan bool) Bridge.AutoChanDie = make(chan bool)
Bridge.Mode = bridgeModeAuto Bridge.Mode = bridgeModeAuto
Bridge.DiscordChannelID = Bridge.BridgeConfig.CID
go Bridge.AutoBridge() go Bridge.AutoBridge()
case "manual": case "manual":
log.Println("bridge starting in manual mode") log.Println("bridge starting in manual mode")
@ -170,6 +175,7 @@ func main() {
case "constant": case "constant":
log.Println("bridge starting in constant mode") log.Println("bridge starting in constant mode")
Bridge.Mode = bridgeModeConstant Bridge.Mode = bridgeModeConstant
Bridge.DiscordChannelID = Bridge.BridgeConfig.CID
go func() { go func() {
for { for {
Bridge.startBridge() Bridge.startBridge()
@ -188,13 +194,14 @@ func main() {
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc <-sc
// Signal the bridge to exit cleanly
Bridge.BridgeDie <- true
log.Println("OS Signal. Bot shutting down") log.Println("OS Signal. Bot shutting down")
// Wait or the bridge to exit cleanly // Wait or the bridge to exit cleanly
Bridge.BridgeMutex.Lock()
if Bridge.Connected { if Bridge.Connected {
//TODO BridgeDie occasionally panics on send to closed channel
Bridge.BridgeDie <- true
Bridge.WaitExit.Wait() Bridge.WaitExit.Wait()
} }
Bridge.BridgeMutex.Unlock()
} }

View File

@ -13,12 +13,10 @@ type MumbleListener struct {
} }
func (l *MumbleListener) mumbleConnect(e *gumble.ConnectEvent) { func (l *MumbleListener) mumbleConnect(e *gumble.ConnectEvent) {
if l.Bridge.BridgeConfig.MumbleChannel != "" { //join specified channel
//join specified channel startingChannel := e.Client.Channels.Find(l.Bridge.BridgeConfig.MumbleChannel...)
startingChannel := e.Client.Channels.Find(l.Bridge.BridgeConfig.MumbleChannel) if startingChannel != nil {
if startingChannel != nil { e.Client.Self.Move(startingChannel)
e.Client.Self.Move(startingChannel)
}
} }
} }
@ -45,22 +43,23 @@ func (l *MumbleListener) mumbleUserChange(e *gumble.UserChangeEvent) {
e.User.Send("Mumble-Discord-Bridge v" + version) e.User.Send("Mumble-Discord-Bridge v" + version)
// Tell the user who is connected to discord // Tell the user who is connected to discord
l.Bridge.DiscordUsersMutex.Lock()
if len(l.Bridge.DiscordUsers) == 0 { if len(l.Bridge.DiscordUsers) == 0 {
e.User.Send("No users connected to Discord") e.User.Send("No users connected to Discord")
} else { } else {
s := "Connected to Discord: " s := "Connected to Discord: "
arr := []string{} arr := []string{}
l.Bridge.DiscordUsersMutex.Lock()
for u := range l.Bridge.DiscordUsers { for u := range l.Bridge.DiscordUsers {
arr = append(arr, l.Bridge.DiscordUsers[u].username) arr = append(arr, l.Bridge.DiscordUsers[u].username)
} }
s = s + strings.Join(arr[:], ",") s = s + strings.Join(arr[:], ",")
l.Bridge.DiscordUsersMutex.Unlock()
e.User.Send(s) e.User.Send(s)
} }
l.Bridge.DiscordUsersMutex.Unlock()
} }
// Send discord a notice // Send discord a notice

View File

@ -29,21 +29,18 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
mutex.Unlock() mutex.Unlock()
go func() { go func() {
// TODO kill go routine on cleanup name := e.User.Name
log.Println("new mumble audio stream", e.User.Name) log.Println("new mumble audio stream", name)
for { for p := range e.C {
select { // log.Println("audio packet", p.Sender.Name, len(p.AudioBuffer))
case p := <-e.C:
// log.Println("audio packet", p.Sender.Name, len(p.AudioBuffer))
// 480 per 10ms // 480 per 10ms
for i := 0; i < len(p.AudioBuffer)/480; i++ { for i := 0; i < len(p.AudioBuffer)/480; i++ {
localMumbleArray <- p.AudioBuffer[480*i : 480*(i+1)] localMumbleArray <- p.AudioBuffer[480*i : 480*(i+1)]
}
} }
} }
log.Println("mumble audio stream ended", name)
}() }()
return
} }
func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, toDiscord chan []int16) { func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, toDiscord chan []int16) {
@ -70,7 +67,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
for i := 0; i < len(fromMumbleArr); i++ { for i := 0; i < len(fromMumbleArr); i++ {
if len(fromMumbleArr[i]) > 0 { if len(fromMumbleArr[i]) > 0 {
sendAudio = true sendAudio = true
if mumbleStreamingArr[i] == false { if !mumbleStreamingArr[i] {
mumbleStreamingArr[i] = true mumbleStreamingArr[i] = true
// log.Println("mumble starting", i) // log.Println("mumble starting", i)
} }
@ -78,7 +75,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
x1 := (<-fromMumbleArr[i]) x1 := (<-fromMumbleArr[i])
internalMixerArr = append(internalMixerArr, x1) internalMixerArr = append(internalMixerArr, x1)
} else { } else {
if mumbleStreamingArr[i] == true { if mumbleStreamingArr[i] {
mumbleStreamingArr[i] = false mumbleStreamingArr[i] = false
// log.Println("mumble stopping", i) // log.Println("mumble stopping", i)
} }