From 5a96c488ee75b405f379d54957a4aca9d698443f Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 29 Dec 2020 15:14:19 -0500 Subject: [PATCH 01/37] initial changes, make it manual linking, show mumble users in status --- config.go | 57 +++++++++++++++++++++++++ handlers.go | 91 +++++++++++++++++++++++++++++++++++++++ main.go | 121 ++++++++-------------------------------------------- yammer.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 103 deletions(-) create mode 100644 config.go create mode 100644 handlers.go create mode 100644 yammer.go diff --git a/config.go b/config.go new file mode 100644 index 0000000..ce6993e --- /dev/null +++ b/config.go @@ -0,0 +1,57 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "strconv" + + "layeh.com/gumble/gumble" +) + +type YammerConfig struct { + Config *gumble.Config + MumbleAddr string + MumbleInsecure bool + Die chan bool + ActiveConns map[string]chan bool +} + +func lookupEnvOrString(key string, defaultVal string) string { + if val, ok := os.LookupEnv(key); ok { + return val + } + return defaultVal +} + +func lookupEnvOrInt(key string, defaultVal int) int { + if val, ok := os.LookupEnv(key); ok { + v, err := strconv.Atoi(val) + if err != nil { + log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) + } + return v + } + return defaultVal +} + +func lookupEnvOrBool(key string, defaultVal bool) bool { + if val, ok := os.LookupEnv(key); ok { + v, err := strconv.ParseBool(val) + if err != nil { + log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) + } + return v + } + return defaultVal +} + +func getConfig(fs *flag.FlagSet) []string { + cfg := make([]string, 0, 10) + fs.VisitAll(func(f *flag.Flag) { + cfg = append(cfg, fmt.Sprintf("%s:%q", f.Name, f.Value.String())) + }) + + return cfg +} diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..a8efb51 --- /dev/null +++ b/handlers.go @@ -0,0 +1,91 @@ +package main + +import ( + "log" + "strings" + + "github.com/bwmarrin/discordgo" +) + +func ready(s *discordgo.Session, event *discordgo.Ready) { + log.Println("READY event registered") +} + +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + + // Ignore all messages created by the bot itself + // This isn't required in this specific example but it's a good practice. + log.Println("Checking message") + if m.Author.ID == s.State.User.ID { + return + } + + if strings.HasPrefix(m.Content, "!yammer link") { + + // 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 guild's current voice states. + for _, vs := range g.VoiceStates { + if vs.UserID == m.Author.ID { + log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) + die := make(chan bool) + YBConfig.ActiveConns[vs.ChannelID] = die + go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, die) + return + } + } + } + + if strings.HasPrefix(m.Content, "!yammer unlink") { + + // 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 guild's current voice states. + for _, vs := range g.VoiceStates { + if vs.UserID == m.Author.ID { + log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) + YBConfig.ActiveConns[vs.ChannelID] <- true + YBConfig.ActiveConns[vs.ChannelID] = nil + return + } + } + } +} + +func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { + + if event.Guild.Unavailable { + return + } + + for _, channel := range event.Guild.Channels { + if channel.ID == event.Guild.ID { + _, _ = s.ChannelMessageSend(channel.ID, "Mumble-Discord bridge is active") + return + } + } +} diff --git a/main.go b/main.go index 0c2c690..8ae56d0 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,12 @@ package main import ( - "crypto/tls" "flag" - "fmt" "log" - "net" "os" "os/signal" "strconv" + "syscall" "time" "github.com/bwmarrin/discordgo" @@ -17,43 +15,7 @@ import ( _ "layeh.com/gumble/opus" ) -func lookupEnvOrString(key string, defaultVal string) string { - if val, ok := os.LookupEnv(key); ok { - return val - } - return defaultVal -} - -func lookupEnvOrInt(key string, defaultVal int) int { - if val, ok := os.LookupEnv(key); ok { - v, err := strconv.Atoi(val) - if err != nil { - log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) - } - return v - } - return defaultVal -} - -func lookupEnvOrBool(key string, defaultVal bool) bool { - if val, ok := os.LookupEnv(key); ok { - v, err := strconv.ParseBool(val) - if err != nil { - log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) - } - return v - } - return defaultVal -} - -func getConfig(fs *flag.FlagSet) []string { - cfg := make([]string, 0, 10) - fs.VisitAll(func(f *flag.Flag) { - cfg = append(cfg, fmt.Sprintf("%s:%q", f.Name, f.Value.String())) - }) - - return cfg -} +var YBConfig *YammerConfig func main() { godotenv.Load() @@ -98,6 +60,12 @@ func main() { // Open Websocket discord.LogLevel = 2 + discord.StateEnabled = true + discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuilds | discordgo.IntentsGuildMessages | discordgo.IntentsGuildVoiceStates) + // register handlers + discord.AddHandler(ready) + discord.AddHandler(messageCreate) + discord.AddHandler(guildCreate) err = discord.Open() if err != nil { log.Println(err) @@ -106,75 +74,22 @@ func main() { defer discord.Close() log.Println("Discord Bot Connected") - - dgv, err := discord.ChannelVoiceJoin(*discordGID, *discordCID, false, false) - if err != nil { - log.Println(err) - return - } - defer dgv.Speaking(false) - defer dgv.Close() - - discord.ShouldReconnectOnError = true - - // MUMBLE Setup - config := gumble.NewConfig() config.Username = *mumbleUsername config.Password = *mumblePassword config.AudioInterval = time.Millisecond * 10 - m := MumbleDuplex{} - - var tlsConfig tls.Config - if *mumbleInsecure { - tlsConfig.InsecureSkipVerify = true + YBConfig = &YammerConfig{ + Config: config, + MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), + MumbleInsecure: *mumbleInsecure, + ActiveConns: make(map[string]chan bool), } - mumble, err := gumble.DialWithDialer(new(net.Dialer),*mumbleAddr+":"+strconv.Itoa(*mumblePort),config, &tlsConfig) + //go startBridge(discord, *discordGID, *discordCID, config, *mumbleAddr+":"+strconv.Itoa(*mumblePort), *mumbleInsecure, die) - if err != nil { - log.Println(err) - return - } - defer mumble.Disconnect() - - // Shared Channels - // Shared channels pass PCM information in 10ms chunks [480]int16 - var toMumble = mumble.AudioOutgoing() - var toDiscord = make(chan []int16, 100) - var die = make(chan bool) - - log.Println("Mumble Connected") - - // Start Passing Between - // Mumble - go m.fromMumbleMixer(toDiscord) - config.AudioListeners.Attach(m) - //Discord - go discordReceivePCM(dgv, die) - go fromDiscordMixer(toMumble) - go discordSendPCM(dgv, toDiscord, die) - - // Wait for Exit Signal - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt) - - go func() { - ticker := time.NewTicker(500 * time.Millisecond) - for { - <-ticker.C - if mumble.State() != 2 { - log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State()))) - die <- true - } - } - }() - - select { - case sig := <-c: - log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig) - case <-die: - log.Println("\nGot internal die request. Terminating Mumble-Bridge") - } + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) + <-sc + log.Println("Bot shutting down") } diff --git a/yammer.go b/yammer.go new file mode 100644 index 0000000..7186a67 --- /dev/null +++ b/yammer.go @@ -0,0 +1,119 @@ +package main + +import ( + "crypto/tls" + "fmt" + "log" + "net" + "os" + "os/signal" + "strconv" + "time" + + "github.com/bwmarrin/discordgo" + "layeh.com/gumble/gumble" +) + +func startBridge(discord *discordgo.Session, discordGID string, discordCID string, config *gumble.Config, mumbleAddr string, mumbleInsecure bool, die chan bool) { + dgv, err := discord.ChannelVoiceJoin(discordGID, discordCID, false, false) + if err != nil { + log.Println(err) + return + } + defer dgv.Speaking(false) + defer dgv.Close() + + discord.ShouldReconnectOnError = true + + // MUMBLE Setup + + m := MumbleDuplex{} + + var tlsConfig tls.Config + if mumbleInsecure { + tlsConfig.InsecureSkipVerify = true + } + + mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig) + + if err != nil { + log.Println(err) + return + } + defer mumble.Disconnect() + + // Shared Channels + // Shared channels pass PCM information in 10ms chunks [480]int16 + var toMumble = mumble.AudioOutgoing() + var toDiscord = make(chan []int16, 100) + + log.Println("Mumble Connected") + + // Start Passing Between + // Mumble + go m.fromMumbleMixer(toDiscord) + config.AudioListeners.Attach(m) + //Discord + go discordReceivePCM(dgv, die) + go fromDiscordMixer(toMumble) + go discordSendPCM(dgv, toDiscord, die) + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt) + + go func() { + ticker := time.NewTicker(500 * time.Millisecond) + for { + <-ticker.C + if mumble.State() != 2 { + log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State()))) + die <- true + } + } + }() + + select { + case sig := <-c: + log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig) + case <-die: + log.Println("\nGot internal die request. Terminating Mumble-Bridge") + dgv.Disconnect() + } +} + +func pingMumble(host, port string, c chan int) { + m, _ := time.ParseDuration("30s") + curr := 0 + log.Println("Started mumble ping loop") + for { + time.Sleep(3 * time.Second) + resp, err := gumble.Ping(host+":"+port, -1, m) + if err != nil { + panic(err) + } + if resp.ConnectedUsers-1 != curr { + curr = resp.ConnectedUsers - 1 + log.Printf("Now %v users in mumble\n", curr) + if curr > 0 { + c <- curr + } + } + } + log.Println("Mumble ping loop broken") +} + +func discordStatusUpdate(dg *discordgo.Session, c chan int) { + status := "" + curr := 0 + log.Println("Started discord control loop") + for { + curr = <-c + log.Println("Updating discord status") + if curr == 0 { + status = "" + } else { + status = fmt.Sprintf("%v users in Mumble\n", curr) + } + dg.UpdateListeningStatus(status) + } + log.Println("Discord control loop broken") +} From 2df391f0d9988ae401413c12fa307cf75667a51e Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 29 Dec 2020 15:23:43 -0500 Subject: [PATCH 02/37] less channel span, add refresh command --- handlers.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/handlers.go b/handlers.go index a8efb51..5a24737 100644 --- a/handlers.go +++ b/handlers.go @@ -3,6 +3,7 @@ package main import ( "log" "strings" + "time" "github.com/bwmarrin/discordgo" ) @@ -74,6 +75,34 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } } } + + if strings.HasPrefix(m.Content, "!yammer refresh") { + + // 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 guild's current voice states. + for _, vs := range g.VoiceStates { + if vs.UserID == m.Author.ID { + log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) + YBConfig.ActiveConns[vs.ChannelID] <- true + time.Sleep(2 * time.Second) + go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, YBConfig.ActiveConns[vs.ChannelID]) + return + } + } + } } func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { @@ -84,7 +113,7 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { for _, channel := range event.Guild.Channels { if channel.ID == event.Guild.ID { - _, _ = s.ChannelMessageSend(channel.ID, "Mumble-Discord bridge is active") + log.Println("Mumble-Discord bridge is active in new guild") return } } From ca1ba1d099b247bec5364610b93c71737649a4d2 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 29 Dec 2020 18:19:44 -0500 Subject: [PATCH 03/37] more cleanup on manual linkage, attempts to get discord->mumble working --- discord.go | 38 +++++++++++++++++++++++++++++++++++--- handlers.go | 7 ++++++- main.go | 2 +- mumble.go | 25 +++++++++++++++++++++---- yammer.go | 14 ++++++++++---- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/discord.go b/discord.go index 8ff77b8..15a7ac4 100644 --- a/discord.go +++ b/discord.go @@ -22,6 +22,10 @@ var discordMutex sync.Mutex var discordMixerMutex sync.Mutex var fromDiscordMap = make(map[uint32]fromDiscord) +func DiscordReset() { + fromDiscordMap = make(map[uint32]fromDiscord) +} + // OnError gets called by dgvoice when an error is encountered. // By default logs to STDERR var OnError = func(str string, err error) { @@ -56,9 +60,15 @@ func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16, die chan b var readyTimeout *time.Timer for { + select { + case <-die: + log.Println("Killing discordSendPCM") + return + default: + } <-ticker.C - if len(pcm) > 1 { + log.Println("looking for speech") if !streaming { v.Speaking(true) streaming = true @@ -88,7 +98,7 @@ func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16, die chan b lastReady = true readyTimeout.Stop() } - + log.Println("sending packets to mumble") v.OpusSend <- opus } else { if streaming { @@ -108,10 +118,19 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { var readyTimeout *time.Timer for { + log.Println("Start loop") + select { + case <-die: + log.Println("killing discord ReceivePCM") + return + default: + } + log.Println("checked for death") if v.Ready == false || v.OpusRecv == nil { if lastReady == true { OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) readyTimeout = time.AfterFunc(30*time.Second, func() { + log.Println("set ready timeout") die <- true }) lastReady = false @@ -123,13 +142,16 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { readyTimeout.Stop() } + log.Println("rec1") p, ok := <-v.OpusRecv + log.Println("rec2") if !ok { log.Println("Opus not ok") continue } discordMutex.Lock() + log.Println("got lock one") _, ok = fromDiscordMap[p.SSRC] discordMutex.Unlock() if !ok { @@ -147,6 +169,7 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { } discordMutex.Lock() + log.Println("got lock 2") p.PCM, err = fromDiscordMap[p.SSRC].decoder.Decode(p.Opus, 960, false) discordMutex.Unlock() if err != nil { @@ -155,17 +178,25 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { } discordMutex.Lock() + log.Println("got lock 3") fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480] fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960] discordMutex.Unlock() + log.Println("finish loop") } } -func fromDiscordMixer(toMumble chan<- gumble.AudioBuffer) { +func fromDiscordMixer(toMumble chan<- gumble.AudioBuffer, die chan bool) { ticker := time.NewTicker(10 * time.Millisecond) sendAudio := false for { + select { + case <-die: + log.Println("killing fromDiscordMixer") + return + default: + } <-ticker.C discordMutex.Lock() @@ -206,6 +237,7 @@ func fromDiscordMixer(toMumble chan<- gumble.AudioBuffer) { if sendAudio { select { case toMumble <- outBuf: + log.Println("sending to mumble") default: log.Println("toMumble buffer full. Dropping packet") } diff --git a/handlers.go b/handlers.go index 5a24737..f275e05 100644 --- a/handlers.go +++ b/handlers.go @@ -71,6 +71,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) YBConfig.ActiveConns[vs.ChannelID] <- true YBConfig.ActiveConns[vs.ChannelID] = nil + MumbleReset() + DiscordReset() return } } @@ -97,7 +99,10 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if vs.UserID == m.Author.ID { log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) YBConfig.ActiveConns[vs.ChannelID] <- true - time.Sleep(2 * time.Second) + MumbleReset() + DiscordReset() + time.Sleep(5 * time.Second) + YBConfig.ActiveConns[vs.ChannelID] = make(chan bool) go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, YBConfig.ActiveConns[vs.ChannelID]) return } diff --git a/main.go b/main.go index 8ae56d0..8ae4c58 100644 --- a/main.go +++ b/main.go @@ -61,7 +61,7 @@ func main() { // Open Websocket discord.LogLevel = 2 discord.StateEnabled = true - discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuilds | discordgo.IntentsGuildMessages | discordgo.IntentsGuildVoiceStates) + discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) // register handlers discord.AddHandler(ready) discord.AddHandler(messageCreate) diff --git a/mumble.go b/mumble.go index 2909c36..fbb12a0 100644 --- a/mumble.go +++ b/mumble.go @@ -14,7 +14,14 @@ var fromMumbleArr []chan gumble.AudioBuffer var mumbleStreamingArr []bool // MumbleDuplex - listenera and outgoing -type MumbleDuplex struct{} +type MumbleDuplex struct { + Close chan bool +} + +func MumbleReset() { + fromMumbleArr = []chan gumble.AudioBuffer{} + mumbleStreamingArr = []bool{} +} // OnAudioStream - Spawn routines to handle incoming packets func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { @@ -27,10 +34,14 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { mumbleStreamingArr = append(mumbleStreamingArr, false) mutex.Unlock() - go func() { + go func(die chan bool) { log.Println("new mumble audio stream", e.User.Name) for { select { + default: + case <-die: + log.Println("Removing mumble audio stream") + return case p := <-e.C: // log.Println("audio packet", p.Sender.Name, len(p.AudioBuffer)) @@ -40,15 +51,21 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { } } } - }() + }(m.Close) return } -func (m MumbleDuplex) fromMumbleMixer(toDiscord chan []int16) { +func (m MumbleDuplex) fromMumbleMixer(toDiscord chan []int16, die chan bool) { ticker := time.NewTicker(10 * time.Millisecond) sendAudio := false for { + select { + case <-die: + log.Println("Killing fromMumbleMixer") + return + default: + } <-ticker.C mutex.Lock() diff --git a/yammer.go b/yammer.go index 7186a67..7bfb6de 100644 --- a/yammer.go +++ b/yammer.go @@ -27,7 +27,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin // MUMBLE Setup - m := MumbleDuplex{} + m := MumbleDuplex{ + Close: make(chan bool), + } var tlsConfig tls.Config if mumbleInsecure { @@ -51,11 +53,11 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin // Start Passing Between // Mumble - go m.fromMumbleMixer(toDiscord) - config.AudioListeners.Attach(m) + go m.fromMumbleMixer(toDiscord, die) + det := config.AudioListeners.Attach(m) //Discord go discordReceivePCM(dgv, die) - go fromDiscordMixer(toMumble) + go fromDiscordMixer(toMumble, die) go discordSendPCM(dgv, toDiscord, die) c := make(chan os.Signal) signal.Notify(c, os.Interrupt) @@ -67,6 +69,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if mumble.State() != 2 { log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State()))) die <- true + m.Close <- true } } }() @@ -76,7 +79,10 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig) case <-die: log.Println("\nGot internal die request. Terminating Mumble-Bridge") + close(toMumble) dgv.Disconnect() + log.Println("Closing mumble threads") + det.Detach() } } From 4b8e6eea55631d25f42c04733b88f649b0c60207 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 15:32:59 -0500 Subject: [PATCH 04/37] use user counting, add discord buffer debuf, intial auto mode --- yammer.go => bridge.go | 47 +++++++++++++++++++++--------- config.go | 16 +++++++++-- discord.go | 27 +++++++++--------- handlers.go | 65 +++++++++++++++++++++++++++++++++++------- main.go | 25 ++++++++++++---- 5 files changed, 133 insertions(+), 47 deletions(-) rename yammer.go => bridge.go (69%) diff --git a/yammer.go b/bridge.go similarity index 69% rename from yammer.go rename to bridge.go index 7bfb6de..1884066 100644 --- a/yammer.go +++ b/bridge.go @@ -22,7 +22,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } defer dgv.Speaking(false) defer dgv.Close() - + Bridge.Connected = true discord.ShouldReconnectOnError = true // MUMBLE Setup @@ -79,41 +79,37 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig) case <-die: log.Println("\nGot internal die request. Terminating Mumble-Bridge") - close(toMumble) dgv.Disconnect() - log.Println("Closing mumble threads") det.Detach() + Bridge.Connected = false } } func pingMumble(host, port string, c chan int) { m, _ := time.ParseDuration("30s") curr := 0 - log.Println("Started mumble ping loop") for { time.Sleep(3 * time.Second) resp, err := gumble.Ping(host+":"+port, -1, m) + curr = resp.ConnectedUsers if err != nil { panic(err) } - if resp.ConnectedUsers-1 != curr { - curr = resp.ConnectedUsers - 1 - log.Printf("Now %v users in mumble\n", curr) - if curr > 0 { - c <- curr - } + if Bridge.Connected { + curr = curr - 1 + } + if curr != Bridge.MumbleUserCount { + Bridge.MumbleUserCount = curr + c <- Bridge.MumbleUserCount } } - log.Println("Mumble ping loop broken") } func discordStatusUpdate(dg *discordgo.Session, c chan int) { status := "" curr := 0 - log.Println("Started discord control loop") for { curr = <-c - log.Println("Updating discord status") if curr == 0 { status = "" } else { @@ -121,5 +117,28 @@ func discordStatusUpdate(dg *discordgo.Session, c chan int) { } dg.UpdateListeningStatus(status) } - log.Println("Discord control loop broken") +} + +func AutoBridge(s *discordgo.Session) { + log.Println("beginning auto mode") + for { + select { + default: + case <-Bridge.AutoChan: + log.Println("ending automode") + return + } + time.Sleep(3 * time.Second) + if !Bridge.Connected && Bridge.MumbleUserCount > 0 && Bridge.DiscordUserCount > 0 { + log.Println("users detected in mumble and discord, bridging") + die := make(chan bool) + Bridge.ActiveConn = die + go startBridge(s, BridgeConf.GID, BridgeConf.CID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + } + log.Printf("DU: %v MU %v\n", Bridge.DiscordUserCount, Bridge.MumbleUserCount) + if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { + log.Println("no one online, killing bridge") + Bridge.ActiveConn <- true + } + } } diff --git a/config.go b/config.go index ce6993e..295f809 100644 --- a/config.go +++ b/config.go @@ -10,12 +10,22 @@ import ( "layeh.com/gumble/gumble" ) -type YammerConfig struct { +type BridgeConfig struct { Config *gumble.Config MumbleAddr string MumbleInsecure bool - Die chan bool - ActiveConns map[string]chan bool + Auto bool + Command string + GID string + CID string +} + +type BridgeState struct { + ActiveConn chan bool + Connected bool + MumbleUserCount int + DiscordUserCount int + AutoChan chan bool } func lookupEnvOrString(key string, defaultVal string) string { diff --git a/discord.go b/discord.go index 15a7ac4..966c66f 100644 --- a/discord.go +++ b/discord.go @@ -98,7 +98,6 @@ func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16, die chan b lastReady = true readyTimeout.Stop() } - log.Println("sending packets to mumble") v.OpusSend <- opus } else { if streaming { @@ -118,14 +117,12 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { var readyTimeout *time.Timer for { - log.Println("Start loop") select { case <-die: log.Println("killing discord ReceivePCM") return default: } - log.Println("checked for death") if v.Ready == false || v.OpusRecv == nil { if lastReady == true { OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) @@ -142,16 +139,13 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { readyTimeout.Stop() } - log.Println("rec1") p, ok := <-v.OpusRecv - log.Println("rec2") if !ok { log.Println("Opus not ok") continue } discordMutex.Lock() - log.Println("got lock one") _, ok = fromDiscordMap[p.SSRC] discordMutex.Unlock() if !ok { @@ -169,20 +163,28 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { } discordMutex.Lock() - log.Println("got lock 2") p.PCM, err = fromDiscordMap[p.SSRC].decoder.Decode(p.Opus, 960, false) discordMutex.Unlock() if err != nil { OnError("Error decoding opus data", err) continue } - + if len(p.PCM) != 960 { + log.Println("Opus size error") + continue + } discordMutex.Lock() - log.Println("got lock 3") - fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480] - fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960] + select { + case fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480]: + default: + log.Println("fromDiscordMap buffer full. Dropping packet") + } + select { + case fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960]: + default: + log.Println("fromDiscordMap buffer full. Dropping packet") + } discordMutex.Unlock() - log.Println("finish loop") } } @@ -237,7 +239,6 @@ func fromDiscordMixer(toMumble chan<- gumble.AudioBuffer, die chan bool) { if sendAudio { select { case toMumble <- outBuf: - log.Println("sending to mumble") default: log.Println("toMumble buffer full. Dropping packet") } diff --git a/handlers.go b/handlers.go index f275e05..4ce0c15 100644 --- a/handlers.go +++ b/handlers.go @@ -20,8 +20,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if m.Author.ID == s.State.User.ID { return } - - if strings.HasPrefix(m.Content, "!yammer link") { + prefix := "!" + BridgeConf.Command + if strings.HasPrefix(m.Content, prefix+" link") { // Find the channel that the message came from. c, err := s.State.Channel(m.ChannelID) @@ -42,14 +42,14 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if vs.UserID == m.Author.ID { log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) die := make(chan bool) - YBConfig.ActiveConns[vs.ChannelID] = die - go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, die) + Bridge.ActiveConn = die + go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) return } } } - if strings.HasPrefix(m.Content, "!yammer unlink") { + if strings.HasPrefix(m.Content, prefix+" unlink") { // Find the channel that the message came from. c, err := s.State.Channel(m.ChannelID) @@ -69,8 +69,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) - YBConfig.ActiveConns[vs.ChannelID] <- true - YBConfig.ActiveConns[vs.ChannelID] = nil + Bridge.ActiveConn <- true + Bridge.ActiveConn = nil MumbleReset() DiscordReset() return @@ -78,7 +78,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } } - if strings.HasPrefix(m.Content, "!yammer refresh") { + if strings.HasPrefix(m.Content, prefix+" refresh") { // Find the channel that the message came from. c, err := s.State.Channel(m.ChannelID) @@ -98,16 +98,27 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) - YBConfig.ActiveConns[vs.ChannelID] <- true + Bridge.ActiveConn <- true MumbleReset() DiscordReset() time.Sleep(5 * time.Second) - YBConfig.ActiveConns[vs.ChannelID] = make(chan bool) - go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, YBConfig.ActiveConns[vs.ChannelID]) + Bridge.ActiveConn = make(chan bool) + go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) return } } } + + if strings.HasPrefix(m.Content, prefix+" auto") { + if BridgeConf.Auto == false { + BridgeConf.Auto = true + Bridge.AutoChan = make(chan bool) + go AutoBridge(s) + } else { + Bridge.AutoChan <- true + BridgeConf.Auto = false + } + } } func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { @@ -123,3 +134,35 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { } } } + +func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { + if event.GuildID == BridgeConf.GID { + if event.ChannelID == BridgeConf.CID { + log.Println("user joined watched discord channel") + Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + } + if event.ChannelID == "" { + //leave event, trigger recount of active users + //TODO when next version of discordgo comes out, switch to PreviousState + g, err := s.State.Guild(event.GuildID) + if err != nil { + // Could not find guild. + return + } + + // Look for current voice states in watched channel + count := 0 + for _, vs := range g.VoiceStates { + if vs.ChannelID == BridgeConf.CID { + count = count + 1 + } + } + if Bridge.DiscordUserCount > count { + log.Println("user left watched discord channel") + Bridge.DiscordUserCount = count + } + } + + } + return +} diff --git a/main.go b/main.go index 8ae4c58..9d53808 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,8 @@ import ( _ "layeh.com/gumble/opus" ) -var YBConfig *YammerConfig +var BridgeConf *BridgeConfig +var Bridge *BridgeState func main() { godotenv.Load() @@ -29,7 +30,7 @@ func main() { discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token") discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") - + discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") flag.Parse() log.Printf("app.config %v\n", getConfig(flag.CommandLine)) @@ -66,6 +67,7 @@ func main() { discord.AddHandler(ready) discord.AddHandler(messageCreate) discord.AddHandler(guildCreate) + discord.AddHandler(voiceUpdate) err = discord.Open() if err != nil { log.Println(err) @@ -74,19 +76,30 @@ func main() { defer discord.Close() log.Println("Discord Bot Connected") + log.Printf("Discord bot looking for command !%v", *discordCommand) config := gumble.NewConfig() config.Username = *mumbleUsername config.Password = *mumblePassword config.AudioInterval = time.Millisecond * 10 - YBConfig = &YammerConfig{ + BridgeConf = &BridgeConfig{ Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, - ActiveConns: make(map[string]chan bool), + Auto: false, + Command: *discordCommand, + GID: *discordGID, + CID: *discordCID, } - - //go startBridge(discord, *discordGID, *discordCID, config, *mumbleAddr+":"+strconv.Itoa(*mumblePort), *mumbleInsecure, die) + Bridge = &BridgeState{ + ActiveConn: make(chan bool), + Connected: false, + MumbleUserCount: 0, + DiscordUserCount: 0, + } + userCount := make(chan int) + go pingMumble(*mumbleAddr, strconv.Itoa(*mumblePort), userCount) + go discordStatusUpdate(discord, userCount) sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) From 77cf32895506f1349597873f98ed09c79b11eb1a Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 15:45:12 -0500 Subject: [PATCH 05/37] add auto_mode flag --- bridge.go | 3 ++- discord.go | 1 - main.go | 8 ++++++-- mumble.go | 3 +++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bridge.go b/bridge.go index 1884066..6efd2ca 100644 --- a/bridge.go +++ b/bridge.go @@ -135,10 +135,11 @@ func AutoBridge(s *discordgo.Session) { Bridge.ActiveConn = die go startBridge(s, BridgeConf.GID, BridgeConf.CID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) } - log.Printf("DU: %v MU %v\n", Bridge.DiscordUserCount, Bridge.MumbleUserCount) if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") Bridge.ActiveConn <- true + MumbleReset() + DiscordReset() } } } diff --git a/discord.go b/discord.go index 966c66f..fb08488 100644 --- a/discord.go +++ b/discord.go @@ -68,7 +68,6 @@ func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16, die chan b } <-ticker.C if len(pcm) > 1 { - log.Println("looking for speech") if !streaming { v.Speaking(true) streaming = true diff --git a/main.go b/main.go index 9d53808..8c85a40 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ func main() { discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") + autoMode := flag.Bool("auto", lookupEnvOrBool("AUTO_MODE", false), "bridge starts in auto mode") flag.Parse() log.Printf("app.config %v\n", getConfig(flag.CommandLine)) @@ -86,7 +87,7 @@ func main() { Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, - Auto: false, + Auto: *autoMode, Command: *discordCommand, GID: *discordGID, CID: *discordCID, @@ -100,7 +101,10 @@ func main() { userCount := make(chan int) go pingMumble(*mumbleAddr, strconv.Itoa(*mumblePort), userCount) go discordStatusUpdate(discord, userCount) - + if *autoMode { + Bridge.AutoChan = make(chan bool) + go AutoBridge(discord) + } sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc diff --git a/mumble.go b/mumble.go index fbb12a0..09141bc 100644 --- a/mumble.go +++ b/mumble.go @@ -105,6 +105,9 @@ func (m MumbleDuplex) fromMumbleMixer(toDiscord chan []int16, die chan bool) { if sendAudio { select { case toDiscord <- outBuf: + case <-die: + log.Println("Killing fromMumbleMixer") + return default: log.Println("toDiscord buffer full. Dropping packet") } From 665d42fc513db281fcc2e8bd538267315f60722e Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 16:19:49 -0500 Subject: [PATCH 06/37] close channels so that everything dies properly --- bridge.go | 19 +++++++++++++++++-- discord.go | 10 ++++++++-- main.go | 8 ++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/bridge.go b/bridge.go index 6efd2ca..c3de13b 100644 --- a/bridge.go +++ b/bridge.go @@ -68,8 +68,20 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin <-ticker.C if mumble.State() != 2 { log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State()))) - die <- true - m.Close <- true + select { + default: + close(die) + case <-die: + //die is already closed + } + + select { + default: + close(m.Close) + case <-m.Close: + //m.Close is already closed + } + return } } }() @@ -81,6 +93,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin log.Println("\nGot internal die request. Terminating Mumble-Bridge") dgv.Disconnect() det.Detach() + close(die) + close(m.Close) + close(toMumble) Bridge.Connected = false } } diff --git a/discord.go b/discord.go index fb08488..77e2233 100644 --- a/discord.go +++ b/discord.go @@ -137,8 +137,14 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { lastReady = true readyTimeout.Stop() } - - p, ok := <-v.OpusRecv + var ok bool + var p *discordgo.Packet + select { + case p, ok = <-v.OpusRecv: + case <-die: + log.Println("killing discord ReceivePCM") + return + } if !ok { log.Println("Opus not ok") continue diff --git a/main.go b/main.go index 8c85a40..c708750 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "log" "os" "os/signal" + "runtime" "strconv" "syscall" "time" @@ -101,6 +102,13 @@ func main() { userCount := make(chan int) go pingMumble(*mumbleAddr, strconv.Itoa(*mumblePort), userCount) go discordStatusUpdate(discord, userCount) + go func() { + for { + time.Sleep(3 * time.Second) + log.Println(runtime.NumGoroutine()) + //pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) + } + }() if *autoMode { Bridge.AutoChan = make(chan bool) go AutoBridge(discord) From 70b0a691b65fdb43ebf7ec8142b1f7bd28009dde Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 16:23:27 -0500 Subject: [PATCH 07/37] remove debug code --- main.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/main.go b/main.go index c708750..8c85a40 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,6 @@ import ( "log" "os" "os/signal" - "runtime" "strconv" "syscall" "time" @@ -102,13 +101,6 @@ func main() { userCount := make(chan int) go pingMumble(*mumbleAddr, strconv.Itoa(*mumblePort), userCount) go discordStatusUpdate(discord, userCount) - go func() { - for { - time.Sleep(3 * time.Second) - log.Println(runtime.NumGoroutine()) - //pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) - } - }() if *autoMode { Bridge.AutoChan = make(chan bool) go AutoBridge(discord) From ba29c23caec1c042cd631ded24b0498bb2510aa3 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 19:49:12 -0500 Subject: [PATCH 08/37] remove legacy ping/status seperation --- bridge.go | 43 +++++++++++++++++++------------------------ main.go | 4 +--- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/bridge.go b/bridge.go index c3de13b..a6bc1fc 100644 --- a/bridge.go +++ b/bridge.go @@ -100,37 +100,32 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } } -func pingMumble(host, port string, c chan int) { - m, _ := time.ParseDuration("30s") +func discordStatusUpdate(dg *discordgo.Session, host, port string) { + status := "" curr := 0 + m, _ := time.ParseDuration("30s") for { time.Sleep(3 * time.Second) resp, err := gumble.Ping(host+":"+port, -1, m) - curr = resp.ConnectedUsers - if err != nil { - panic(err) - } - if Bridge.Connected { - curr = curr - 1 - } - if curr != Bridge.MumbleUserCount { - Bridge.MumbleUserCount = curr - c <- Bridge.MumbleUserCount - } - } -} -func discordStatusUpdate(dg *discordgo.Session, c chan int) { - status := "" - curr := 0 - for { - curr = <-c - if curr == 0 { - status = "" + if err != nil { + log.Printf("error pinging mumble server %v\n", err) + dg.UpdateListeningStatus("an error pinging mumble") } else { - status = fmt.Sprintf("%v users in Mumble\n", curr) + curr = resp.ConnectedUsers + if Bridge.Connected { + curr = curr - 1 + } + if curr != Bridge.MumbleUserCount { + Bridge.MumbleUserCount = curr + } + if curr == 0 { + status = "" + } else { + status = fmt.Sprintf("%v users in Mumble\n", curr) + } + dg.UpdateListeningStatus(status) } - dg.UpdateListeningStatus(status) } } diff --git a/main.go b/main.go index 8c85a40..4a6442e 100644 --- a/main.go +++ b/main.go @@ -98,9 +98,7 @@ func main() { MumbleUserCount: 0, DiscordUserCount: 0, } - userCount := make(chan int) - go pingMumble(*mumbleAddr, strconv.Itoa(*mumblePort), userCount) - go discordStatusUpdate(discord, userCount) + go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) if *autoMode { Bridge.AutoChan = make(chan bool) go AutoBridge(discord) From f63b9d005cc13053aede1f2850c7556962cc7dcb Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:20:03 -0500 Subject: [PATCH 09/37] send message in mumble when user joins in discord --- bridge.go | 20 ++++++++++++++++++++ config.go | 9 +-------- handlers.go | 7 +++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/bridge.go b/bridge.go index a6bc1fc..571c0cf 100644 --- a/bridge.go +++ b/bridge.go @@ -14,6 +14,16 @@ import ( "layeh.com/gumble/gumble" ) +type BridgeState struct { + ActiveConn chan bool + Connected bool + Client *gumble.Client + CurrentChannel *gumble.Channel + MumbleUserCount int + DiscordUserCount int + AutoChan chan bool +} + func startBridge(discord *discordgo.Session, discordGID string, discordCID string, config *gumble.Config, mumbleAddr string, mumbleInsecure bool, die chan bool) { dgv, err := discord.ChannelVoiceJoin(discordGID, discordCID, false, false) if err != nil { @@ -43,6 +53,13 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin return } defer mumble.Disconnect() + Bridge.Client = mumble + if BridgeConf.MumbleChannel != "" { + //join specified channel + startingChannel := mumble.Channels.Find(BridgeConf.MumbleChannel) + mumble.Self.Move(startingChannel) + Bridge.CurrentChannel = startingChannel + } // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 @@ -97,6 +114,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin close(m.Close) close(toMumble) Bridge.Connected = false + Bridge.Client = nil + Bridge.MumbleUserCount = 0 + Bridge.DiscordUserCount = 0 } } diff --git a/config.go b/config.go index 295f809..b71d926 100644 --- a/config.go +++ b/config.go @@ -14,20 +14,13 @@ type BridgeConfig struct { Config *gumble.Config MumbleAddr string MumbleInsecure bool + MumbleChannel string Auto bool Command string GID string CID string } -type BridgeState struct { - ActiveConn chan bool - Connected bool - MumbleUserCount int - DiscordUserCount int - AutoChan chan bool -} - func lookupEnvOrString(key string, defaultVal string) string { if val, ok := os.LookupEnv(key); ok { return val diff --git a/handlers.go b/handlers.go index 4ce0c15..482ec08 100644 --- a/handlers.go +++ b/handlers.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "log" "strings" "time" @@ -139,6 +140,12 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { if event.GuildID == BridgeConf.GID { if event.ChannelID == BridgeConf.CID { log.Println("user joined watched discord channel") + u, err := s.User(event.UserID) + if err != nil { + log.Printf("error looking up user for uid %v", event.UserID) + } else { + Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + } Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 } if event.ChannelID == "" { From a86586ba5752b23f3ad393ef4f3a148d35c7cef0 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:21:55 -0500 Subject: [PATCH 10/37] actually use MumbleChannel config option --- main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 4a6442e..9c96be2 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ func main() { mumbleUsername := flag.String("mumble-username", lookupEnvOrString("MUMBLE_USERNAME", "discord-bridge"), "MUMBLE_USERNAME, mumble username") mumblePassword := flag.String("mumble-password", lookupEnvOrString("MUMBLE_PASSWORD", ""), "MUMBLE_PASSWORD, mumble password, optional") mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), "mumble insecure, env alt MUMBLE_INSECURE") - + mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "mumble channel to start in") discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token") discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") @@ -87,6 +87,7 @@ func main() { Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, + MumbleChannel: *mumbleChannel, Auto: *autoMode, Command: *discordCommand, GID: *discordGID, From f1f59116b5af9b97bb7a85c60e98612e75f13995 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:25:51 -0500 Subject: [PATCH 11/37] don't try to send message before we connect to mumble --- handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers.go b/handlers.go index 482ec08..376fc06 100644 --- a/handlers.go +++ b/handlers.go @@ -143,7 +143,7 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) - } else { + } else if Bridge.Connected { Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) } Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 From 8257bf55ab3291c6f1b8b1154ba8c234d761e9f2 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:29:56 -0500 Subject: [PATCH 12/37] only care about joins, not mutes/deafens --- handlers.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/handlers.go b/handlers.go index 376fc06..4c08e39 100644 --- a/handlers.go +++ b/handlers.go @@ -139,6 +139,18 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { if event.GuildID == BridgeConf.GID { if event.ChannelID == BridgeConf.CID { + //check to see if this is actually a new user + g, err := s.State.Guild(event.GuildID) + if err != nil { + log.Println("Could not find guild while checking VoiceStateUpdate") + return + } + for _, vs := range g.VoiceStates { + if vs.UserID == event.UserID { + //user is already in channel (and is probably just muting/etc), ignore + return + } + } log.Println("user joined watched discord channel") u, err := s.User(event.UserID) if err != nil { From b782efdf048d441ec3d7601eceba7202c372ff23 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:48:34 -0500 Subject: [PATCH 13/37] keep track of discord users in set --- bridge.go | 21 +++++++++++++++++++++ handlers.go | 28 ++++++++++++++-------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/bridge.go b/bridge.go index 571c0cf..170bf8a 100644 --- a/bridge.go +++ b/bridge.go @@ -19,6 +19,7 @@ type BridgeState struct { Connected bool Client *gumble.Client CurrentChannel *gumble.Channel + DiscordUsers map[string]bool MumbleUserCount int DiscordUserCount int AutoChan chan bool @@ -103,6 +104,25 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } }() + //Setup initial discord state + g, err := discord.State.Guild(discordGID) + Bridge.DiscordUsers = make(map[string]bool) + if err != nil { + log.Println("error finding guild") + panic(err) + } + for _, vs := range g.VoiceStates { + if vs.ChannelID == discordCID { + Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + u, err := discord.User(vs.UserID) + if err != nil { + log.Println("error looking up username") + Bridge.DiscordUsers[u.Username] = true + Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + } + } + } + select { case sig := <-c: log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig) @@ -117,6 +137,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin Bridge.Client = nil Bridge.MumbleUserCount = 0 Bridge.DiscordUserCount = 0 + Bridge.DiscordUsers = nil } } diff --git a/handlers.go b/handlers.go index 4c08e39..f926a59 100644 --- a/handlers.go +++ b/handlers.go @@ -139,23 +139,18 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { if event.GuildID == BridgeConf.GID { if event.ChannelID == BridgeConf.CID { - //check to see if this is actually a new user - g, err := s.State.Guild(event.GuildID) - if err != nil { - log.Println("Could not find guild while checking VoiceStateUpdate") - return - } - for _, vs := range g.VoiceStates { - if vs.UserID == event.UserID { - //user is already in channel (and is probably just muting/etc), ignore - return - } - } - log.Println("user joined watched discord channel") + //get user u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) - } else if Bridge.Connected { + } + //check to see if actually new user + if Bridge.DiscordUsers[u.Username] { + //not actually new user + return + } + log.Println("user joined watched discord channel") + if Bridge.Connected { Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) } Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 @@ -177,6 +172,11 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } } if Bridge.DiscordUserCount > count { + u, err := s.User(event.UserID) + if err != nil { + log.Printf("error looking up user for uid %v", event.UserID) + } + delete(Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") Bridge.DiscordUserCount = count } From 41e864a0f3e2c2a409cf50893d15c1e804869940 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 20:52:30 -0500 Subject: [PATCH 14/37] send message when user leaves too --- handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/handlers.go b/handlers.go index f926a59..e304daf 100644 --- a/handlers.go +++ b/handlers.go @@ -178,6 +178,7 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } delete(Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") + Bridge.CurrentChannel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) Bridge.DiscordUserCount = count } } From 04f56bd3e07597dd616952ccdf7ae0e3e98c8e87 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 3 Jan 2021 21:20:01 -0500 Subject: [PATCH 15/37] update readme --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c25fc88..16fcbc2 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Usage of mumble-discord-bridge: DISCORD_GID, discord gid -discord-token string DISCORD_TOKEN, discord bot token + -discord-command string + DISCORD_COMMAND, the string to look for when manually entering commands in Discord (in the form of !DISCORD_COMMAND) -mumble-address string MUMBLE_ADDRESS, mumble server address, example example.com -mumble-password string @@ -28,8 +30,24 @@ Usage of mumble-discord-bridge: MUMBLE_USERNAME, mumble username (default "discord-bridge") -mumble-insecure bool MUMBLE_INSECURE, allow connection to insecure (invalid TLS cert) mumble server + -mumble-channel string + MUMBLE_CHANNEL, pick what channel the bridge joins in Mumble. Must be a direct child of Root. + -auto-mode bool + AUTO_MODE, enables the bridge to automatically start if there's users in both Discord and Mumble ``` +The bridge can be manually controlled in Discord with the following commands: + +``` +!DISCORD_COMMAND link + Commands the bridge to join the Discord channel the user is in and the Mumble server +!DISCORD_COMMAND unlink + Commands the bridge to leave the Discord channel the user is in and the Mumble server +!DISCORD_COMMAND refresh + Commands the bridge to unlink, then link again. +!DISCORD_COMMAND auto + Toggle AUTO_MODE +``` ## Setup ### Creating a Discord Bot @@ -110,4 +128,4 @@ Please consider opening an issue to discuss features and ideas. The project would not have been possible without: - [gumble](https://github.com/layeh/gumble) -- [discordgo](https://github.com/bwmarrin/discordgo) \ No newline at end of file +- [discordgo](https://github.com/bwmarrin/discordgo) From 8f5caaf834d26f7f4af2b6ad8b291a0fc207753a Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 4 Jan 2021 20:53:03 -0500 Subject: [PATCH 16/37] actually add user to set --- handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/handlers.go b/handlers.go index e304daf..6118458 100644 --- a/handlers.go +++ b/handlers.go @@ -154,6 +154,7 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) } Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + Bridge.DiscordUsers[u.Username] = true } if event.ChannelID == "" { //leave event, trigger recount of active users From 2aa1a32d643984befe451074d54be9257ad92f2e Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 4 Jan 2021 21:54:29 -0500 Subject: [PATCH 17/37] don't add users before the bridge starts --- handlers.go | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/handlers.go b/handlers.go index 6118458..b7bcca0 100644 --- a/handlers.go +++ b/handlers.go @@ -152,9 +152,9 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { log.Println("user joined watched discord channel") if Bridge.Connected { Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + Bridge.DiscordUsers[u.Username] = true } Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 - Bridge.DiscordUsers[u.Username] = true } if event.ChannelID == "" { //leave event, trigger recount of active users diff --git a/main.go b/main.go index 9c96be2..bd20318 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func main() { discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") - autoMode := flag.Bool("auto", lookupEnvOrBool("AUTO_MODE", false), "bridge starts in auto mode") + autoMode := flag.Bool("auto-mode", lookupEnvOrBool("AUTO_MODE", false), "bridge starts in auto mode") flag.Parse() log.Printf("app.config %v\n", getConfig(flag.CommandLine)) From b7b1065abbdbe3181291299f24dc6024949ad6b2 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 11:41:33 -0500 Subject: [PATCH 18/37] use mumble client more,finally get user count right --- bridge.go | 14 ++++++-------- handlers.go | 9 ++++++--- main.go | 5 +++++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/bridge.go b/bridge.go index 170bf8a..cd57238 100644 --- a/bridge.go +++ b/bridge.go @@ -18,7 +18,6 @@ type BridgeState struct { ActiveConn chan bool Connected bool Client *gumble.Client - CurrentChannel *gumble.Channel DiscordUsers map[string]bool MumbleUserCount int DiscordUserCount int @@ -33,7 +32,6 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } defer dgv.Speaking(false) defer dgv.Close() - Bridge.Connected = true discord.ShouldReconnectOnError = true // MUMBLE Setup @@ -58,10 +56,10 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if BridgeConf.MumbleChannel != "" { //join specified channel startingChannel := mumble.Channels.Find(BridgeConf.MumbleChannel) - mumble.Self.Move(startingChannel) - Bridge.CurrentChannel = startingChannel + if startingChannel != nil { + mumble.Self.Move(startingChannel) + } } - // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 var toMumble = mumble.AudioOutgoing() @@ -106,7 +104,6 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin //Setup initial discord state g, err := discord.State.Guild(discordGID) - Bridge.DiscordUsers = make(map[string]bool) if err != nil { log.Println("error finding guild") panic(err) @@ -118,10 +115,11 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if err != nil { log.Println("error looking up username") Bridge.DiscordUsers[u.Username] = true - Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) } } } + Bridge.Connected = true select { case sig := <-c: @@ -137,7 +135,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin Bridge.Client = nil Bridge.MumbleUserCount = 0 Bridge.DiscordUserCount = 0 - Bridge.DiscordUsers = nil + Bridge.DiscordUsers = make(map[string]bool) } } diff --git a/handlers.go b/handlers.go index b7bcca0..90ad45d 100644 --- a/handlers.go +++ b/handlers.go @@ -151,9 +151,10 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } log.Println("user joined watched discord channel") if Bridge.Connected { - Bridge.CurrentChannel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) - Bridge.DiscordUsers[u.Username] = true + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) } + Bridge.DiscordUsers[u.Username] = true + log.Println(Bridge.DiscordUsers) Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 } if event.ChannelID == "" { @@ -179,7 +180,9 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } delete(Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") - Bridge.CurrentChannel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + if Bridge.Connected { + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + } Bridge.DiscordUserCount = count } } diff --git a/main.go b/main.go index bd20318..8fca563 100644 --- a/main.go +++ b/main.go @@ -51,6 +51,10 @@ func main() { if *discordCID == "" { log.Fatalln("missing discord cid") } + err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) + if err != nil { + log.Println("Unable to set priority. ", err) + } // DISCORD Setup @@ -98,6 +102,7 @@ func main() { Connected: false, MumbleUserCount: 0, DiscordUserCount: 0, + DiscordUsers: make(map[string]bool), } go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) if *autoMode { From d75fff08f2a5a804194465be8a8fdaf200cd890c Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 12:16:51 -0500 Subject: [PATCH 19/37] readme update --- README.md | 25 ++++++++++++++++++------- config.go | 10 +++++++++- handlers.go | 12 +++++++----- main.go | 26 ++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 16fcbc2..038665d 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ The binary will also attempt to load .env file located in the working directory. ```bash Usage of mumble-discord-bridge: -discord-cid string - DISCORD_CID, discord cid + DISCORD_CID, discord channel ID -discord-gid string - DISCORD_GID, discord gid + DISCORD_GID, discord guild ID -discord-token string DISCORD_TOKEN, discord bot token -discord-command string @@ -32,13 +32,24 @@ Usage of mumble-discord-bridge: MUMBLE_INSECURE, allow connection to insecure (invalid TLS cert) mumble server -mumble-channel string MUMBLE_CHANNEL, pick what channel the bridge joins in Mumble. Must be a direct child of Root. - -auto-mode bool - AUTO_MODE, enables the bridge to automatically start if there's users in both Discord and Mumble + -mode string + MODE, determines what mode the bridge starts in ``` -The bridge can be manually controlled in Discord with the following commands: - +The bridge can be run with the follow modes: +```bash + auto + The bridge starts up but does not connect immediately. It can be either manually linked (see below) or will join the voice channels when there's at least one person on each side. + The bridge will leave both voice channels once there is no one on either end + manual + The bridge starts up but does not connect immediately. It will join the voice channels when issued the link command and will leave with the unlink command + constant + The bridge starts up and immediately connects to both Discord and Mumble voice channels. It can not be controlled in this mode and quits when the program is stopped ``` + +In "auto" or "manual" modes, the bridge can be controlled in Discord with the following commands: + +```bash !DISCORD_COMMAND link Commands the bridge to join the Discord channel the user is in and the Mumble server !DISCORD_COMMAND unlink @@ -46,7 +57,7 @@ The bridge can be manually controlled in Discord with the following commands: !DISCORD_COMMAND refresh Commands the bridge to unlink, then link again. !DISCORD_COMMAND auto - Toggle AUTO_MODE + Toggle between manual and auto mode ``` ## Setup diff --git a/config.go b/config.go index b71d926..5fd9885 100644 --- a/config.go +++ b/config.go @@ -10,12 +10,20 @@ import ( "layeh.com/gumble/gumble" ) +type BridgeMode int + +const ( + BridgeModeAuto BridgeMode = iota + BridgeModeManual + BridgeModeConstant +) + type BridgeConfig struct { Config *gumble.Config MumbleAddr string MumbleInsecure bool MumbleChannel string - Auto bool + Mode BridgeMode Command string GID string CID string diff --git a/handlers.go b/handlers.go index 90ad45d..c8451a3 100644 --- a/handlers.go +++ b/handlers.go @@ -15,9 +15,11 @@ func ready(s *discordgo.Session, event *discordgo.Ready) { func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + if BridgeConf.Mode == BridgeModeConstant { + return + } + // Ignore all messages created by the bot itself - // This isn't required in this specific example but it's a good practice. - log.Println("Checking message") if m.Author.ID == s.State.User.ID { return } @@ -111,13 +113,13 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } if strings.HasPrefix(m.Content, prefix+" auto") { - if BridgeConf.Auto == false { - BridgeConf.Auto = true + if BridgeConf.Mode != BridgeModeAuto { + BridgeConf.Mode = BridgeModeAuto Bridge.AutoChan = make(chan bool) go AutoBridge(s) } else { Bridge.AutoChan <- true - BridgeConf.Auto = false + BridgeConf.Mode = BridgeModeManual } } } diff --git a/main.go b/main.go index 8fca563..03b32f9 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func main() { discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") - autoMode := flag.Bool("auto-mode", lookupEnvOrBool("AUTO_MODE", false), "bridge starts in auto mode") + mode := flag.String("mode", lookupEnvOrString("MODE", "manual"), "determine which mode the bridge starts in") flag.Parse() log.Printf("app.config %v\n", getConfig(flag.CommandLine)) @@ -51,6 +51,9 @@ func main() { if *discordCID == "" { log.Fatalln("missing discord cid") } + if *mode == "" { + log.Fatalln("missing mode set") + } err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) if err != nil { log.Println("Unable to set priority. ", err) @@ -82,6 +85,7 @@ func main() { log.Println("Discord Bot Connected") log.Printf("Discord bot looking for command !%v", *discordCommand) + // Mumble setup config := gumble.NewConfig() config.Username = *mumbleUsername config.Password = *mumblePassword @@ -92,7 +96,7 @@ func main() { MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, MumbleChannel: *mumbleChannel, - Auto: *autoMode, + Mode: -1, Command: *discordCommand, GID: *discordGID, CID: *discordCID, @@ -104,11 +108,25 @@ func main() { DiscordUserCount: 0, DiscordUsers: make(map[string]bool), } - go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) - if *autoMode { + switch *mode { + case "auto": + log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) + BridgeConf.Mode = BridgeModeAuto go AutoBridge(discord) + case "manual": + log.Println("bridge starting in manual mode") + BridgeConf.Mode = BridgeModeManual + case "constant": + log.Println("bridge starting in constant mode") + BridgeConf.Mode = BridgeModeConstant + go startBridge(discord, *discordGID, *discordCID, config, BridgeConf.MumbleAddr, *mumbleInsecure, make(chan bool)) + default: + discord.Close() + log.Fatalln("invalid bridge mode set") } + + go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc From c469a65ed6a8bc1522579eab39fde797d51c1015 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 13:16:03 -0500 Subject: [PATCH 20/37] begin switching to mumble event listeners --- bridge.go | 13 +++++-------- handlers.go | 11 +++++++++++ main.go | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/bridge.go b/bridge.go index cd57238..d015741 100644 --- a/bridge.go +++ b/bridge.go @@ -12,6 +12,7 @@ import ( "github.com/bwmarrin/discordgo" "layeh.com/gumble/gumble" + "layeh.com/gumble/gumbleutil" ) type BridgeState struct { @@ -44,7 +45,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if mumbleInsecure { tlsConfig.InsecureSkipVerify = true } - + config.Attach(gumbleutil.Listener{ + Connect: mumbleConnect, + }) mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig) if err != nil { @@ -53,13 +56,6 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } defer mumble.Disconnect() Bridge.Client = mumble - if BridgeConf.MumbleChannel != "" { - //join specified channel - startingChannel := mumble.Channels.Find(BridgeConf.MumbleChannel) - if startingChannel != nil { - mumble.Self.Move(startingChannel) - } - } // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 var toMumble = mumble.AudioOutgoing() @@ -71,6 +67,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin // Mumble go m.fromMumbleMixer(toDiscord, die) det := config.AudioListeners.Attach(m) + //Discord go discordReceivePCM(dgv, die) go fromDiscordMixer(toMumble, die) diff --git a/handlers.go b/handlers.go index c8451a3..6e33cb7 100644 --- a/handlers.go +++ b/handlers.go @@ -7,6 +7,7 @@ import ( "time" "github.com/bwmarrin/discordgo" + "layeh.com/gumble/gumble" ) func ready(s *discordgo.Session, event *discordgo.Ready) { @@ -192,3 +193,13 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } return } + +func mumbleConnect(e *gumble.ConnectEvent) { + if BridgeConf.MumbleChannel != "" { + //join specified channel + startingChannel := e.Client.Channels.Find(BridgeConf.MumbleChannel) + if startingChannel != nil { + e.Client.Self.Move(startingChannel) + } + } +} diff --git a/main.go b/main.go index 03b32f9..10dad71 100644 --- a/main.go +++ b/main.go @@ -91,6 +91,7 @@ func main() { config.Password = *mumblePassword config.AudioInterval = time.Millisecond * 10 + // Bridge setup BridgeConf = &BridgeConfig{ Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), From 1e8a0a4165c8c396aac069580e3d0c9c857226ea Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 19:00:28 -0500 Subject: [PATCH 21/37] count how many users are actually in channel --- bridge.go | 10 ++++++++-- handlers.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/bridge.go b/bridge.go index d015741..1e6bffc 100644 --- a/bridge.go +++ b/bridge.go @@ -20,6 +20,7 @@ type BridgeState struct { Connected bool Client *gumble.Client DiscordUsers map[string]bool + MumbleUsers map[string]bool MumbleUserCount int DiscordUserCount int AutoChan chan bool @@ -46,7 +47,8 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin tlsConfig.InsecureSkipVerify = true } config.Attach(gumbleutil.Listener{ - Connect: mumbleConnect, + Connect: mumbleConnect, + UserChange: mumbleUserChange, }) mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig) @@ -158,7 +160,11 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string) { if curr == 0 { status = "" } else { - status = fmt.Sprintf("%v users in Mumble\n", curr) + if len(Bridge.MumbleUsers) > 0 { + status = fmt.Sprintf("%v/%v users in Mumble\n", len(Bridge.MumbleUsers), curr) + } else { + status = fmt.Sprintf("%v users in Mumble\n", curr) + } } dg.UpdateListeningStatus(status) } diff --git a/handlers.go b/handlers.go index 6e33cb7..cc7a652 100644 --- a/handlers.go +++ b/handlers.go @@ -203,3 +203,17 @@ func mumbleConnect(e *gumble.ConnectEvent) { } } } + +func mumbleUserChange(e *gumble.UserChangeEvent) { + if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { + Bridge.MumbleUsers = make(map[string]bool) + for _, user := range Bridge.Client.Self.Channel.Users { + //note, this might be too slow for really really big channels? + //event listeners block while processing + //also probably bad to rebuild the set every user change. + if user.Name != Bridge.Client.Self.Name { + Bridge.MumbleUsers[user.Name] = true + } + } + } +} From 4ae61769917e99510b6be8aa42c85596db1f2fb0 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 19:08:17 -0500 Subject: [PATCH 22/37] use gumble.Client.Do for sending messages thread safely --- bridge.go | 4 +++- handlers.go | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bridge.go b/bridge.go index 1e6bffc..48a84b8 100644 --- a/bridge.go +++ b/bridge.go @@ -114,7 +114,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if err != nil { log.Println("error looking up username") Bridge.DiscordUsers[u.Username] = true - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + Bridge.Client.Do(func() { + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + }) } } } diff --git a/handlers.go b/handlers.go index cc7a652..256108f 100644 --- a/handlers.go +++ b/handlers.go @@ -154,7 +154,9 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { } log.Println("user joined watched discord channel") if Bridge.Connected { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + Bridge.Client.Do(func() { + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + }) } Bridge.DiscordUsers[u.Username] = true log.Println(Bridge.DiscordUsers) @@ -184,7 +186,9 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { delete(Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") if Bridge.Connected { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + Bridge.Client.Do(func() { + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + }) } Bridge.DiscordUserCount = count } From bcd7ee97ba6ff6fbf60d9754d29ca00d039b4af8 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 19:43:55 -0500 Subject: [PATCH 23/37] mumble event listeners use non-global state --- bridge.go | 18 +++++++++--------- handlers.go | 23 ++++++++++++++--------- main.go | 5 +++-- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/bridge.go b/bridge.go index 48a84b8..b8a7caf 100644 --- a/bridge.go +++ b/bridge.go @@ -26,7 +26,7 @@ type BridgeState struct { AutoChan chan bool } -func startBridge(discord *discordgo.Session, discordGID string, discordCID string, config *gumble.Config, mumbleAddr string, mumbleInsecure bool, die chan bool) { +func startBridge(discord *discordgo.Session, discordGID string, discordCID string, l *Listener, die chan bool) { dgv, err := discord.ChannelVoiceJoin(discordGID, discordCID, false, false) if err != nil { log.Println(err) @@ -43,14 +43,14 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } var tlsConfig tls.Config - if mumbleInsecure { + if l.BridgeConf.MumbleInsecure { tlsConfig.InsecureSkipVerify = true } - config.Attach(gumbleutil.Listener{ - Connect: mumbleConnect, - UserChange: mumbleUserChange, + l.BridgeConf.Config.Attach(gumbleutil.Listener{ + Connect: l.mumbleConnect, + UserChange: l.mumbleUserChange, }) - mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig) + mumble, err := gumble.DialWithDialer(new(net.Dialer), l.BridgeConf.MumbleAddr, l.BridgeConf.Config, &tlsConfig) if err != nil { log.Println(err) @@ -68,7 +68,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin // Start Passing Between // Mumble go m.fromMumbleMixer(toDiscord, die) - det := config.AudioListeners.Attach(m) + det := l.BridgeConf.Config.AudioListeners.Attach(m) //Discord go discordReceivePCM(dgv, die) @@ -173,7 +173,7 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string) { } } -func AutoBridge(s *discordgo.Session) { +func AutoBridge(s *discordgo.Session, l *Listener) { log.Println("beginning auto mode") for { select { @@ -187,7 +187,7 @@ func AutoBridge(s *discordgo.Session) { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) Bridge.ActiveConn = die - go startBridge(s, BridgeConf.GID, BridgeConf.CID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + go startBridge(s, BridgeConf.GID, BridgeConf.CID, l, die) } if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") diff --git a/handlers.go b/handlers.go index 256108f..b0a4c25 100644 --- a/handlers.go +++ b/handlers.go @@ -10,6 +10,11 @@ import ( "layeh.com/gumble/gumble" ) +type Listener struct { + BridgeConf *BridgeConfig + Bridge *BridgeState +} + func ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") } @@ -47,7 +52,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) die := make(chan bool) Bridge.ActiveConn = die - go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) return } } @@ -107,7 +112,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { DiscordReset() time.Sleep(5 * time.Second) Bridge.ActiveConn = make(chan bool) - go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) + //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) return } } @@ -117,7 +122,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if BridgeConf.Mode != BridgeModeAuto { BridgeConf.Mode = BridgeModeAuto Bridge.AutoChan = make(chan bool) - go AutoBridge(s) + //go AutoBridge(s) } else { Bridge.AutoChan <- true BridgeConf.Mode = BridgeModeManual @@ -198,8 +203,8 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { return } -func mumbleConnect(e *gumble.ConnectEvent) { - if BridgeConf.MumbleChannel != "" { +func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { + if l.BridgeConf.MumbleChannel != "" { //join specified channel startingChannel := e.Client.Channels.Find(BridgeConf.MumbleChannel) if startingChannel != nil { @@ -208,15 +213,15 @@ func mumbleConnect(e *gumble.ConnectEvent) { } } -func mumbleUserChange(e *gumble.UserChangeEvent) { +func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { Bridge.MumbleUsers = make(map[string]bool) - for _, user := range Bridge.Client.Self.Channel.Users { + for _, user := range l.Bridge.Client.Self.Channel.Users { //note, this might be too slow for really really big channels? //event listeners block while processing //also probably bad to rebuild the set every user change. - if user.Name != Bridge.Client.Self.Name { - Bridge.MumbleUsers[user.Name] = true + if user.Name != l.Bridge.Client.Self.Name { + l.Bridge.MumbleUsers[user.Name] = true } } } diff --git a/main.go b/main.go index 10dad71..b4d0fff 100644 --- a/main.go +++ b/main.go @@ -109,19 +109,20 @@ func main() { DiscordUserCount: 0, DiscordUsers: make(map[string]bool), } + l := &Listener{BridgeConf, Bridge} switch *mode { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) BridgeConf.Mode = BridgeModeAuto - go AutoBridge(discord) + //go AutoBridge(discord,l) case "manual": log.Println("bridge starting in manual mode") BridgeConf.Mode = BridgeModeManual case "constant": log.Println("bridge starting in constant mode") BridgeConf.Mode = BridgeModeConstant - go startBridge(discord, *discordGID, *discordCID, config, BridgeConf.MumbleAddr, *mumbleInsecure, make(chan bool)) + go startBridge(discord, *discordGID, *discordCID, l, make(chan bool)) default: discord.Close() log.Fatalln("invalid bridge mode set") From ac800167f6872c0e24895a3c65eac5451dfe075b Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 20:21:08 -0500 Subject: [PATCH 24/37] discord event listeners use non-global state --- handlers.go | 38 +++++++++++++++++++------------------- main.go | 44 +++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/handlers.go b/handlers.go index b0a4c25..2d0b4b1 100644 --- a/handlers.go +++ b/handlers.go @@ -15,13 +15,13 @@ type Listener struct { Bridge *BridgeState } -func ready(s *discordgo.Session, event *discordgo.Ready) { +func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") } -func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { +func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - if BridgeConf.Mode == BridgeModeConstant { + if l.BridgeConf.Mode == BridgeModeConstant { return } @@ -29,7 +29,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if m.Author.ID == s.State.User.ID { return } - prefix := "!" + BridgeConf.Command + prefix := "!" + l.BridgeConf.Command if strings.HasPrefix(m.Content, prefix+" link") { // Find the channel that the message came from. @@ -51,8 +51,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if vs.UserID == m.Author.ID { log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) die := make(chan bool) - Bridge.ActiveConn = die - //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + l.Bridge.ActiveConn = die + go startBridge(s, g.ID, vs.ChannelID, l, die) return } } @@ -78,8 +78,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) - Bridge.ActiveConn <- true - Bridge.ActiveConn = nil + l.Bridge.ActiveConn <- true + l.Bridge.ActiveConn = nil MumbleReset() DiscordReset() return @@ -107,30 +107,30 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) - Bridge.ActiveConn <- true + l.Bridge.ActiveConn <- true MumbleReset() DiscordReset() time.Sleep(5 * time.Second) - Bridge.ActiveConn = make(chan bool) - //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) + l.Bridge.ActiveConn = make(chan bool) + go startBridge(s, g.ID, vs.ChannelID, l, l.Bridge.ActiveConn) return } } } if strings.HasPrefix(m.Content, prefix+" auto") { - if BridgeConf.Mode != BridgeModeAuto { - BridgeConf.Mode = BridgeModeAuto - Bridge.AutoChan = make(chan bool) - //go AutoBridge(s) + if l.BridgeConf.Mode != BridgeModeAuto { + l.BridgeConf.Mode = BridgeModeAuto + l.Bridge.AutoChan = make(chan bool) + go AutoBridge(s, l) } else { - Bridge.AutoChan <- true - BridgeConf.Mode = BridgeModeManual + l.Bridge.AutoChan <- true + l.BridgeConf.Mode = BridgeModeManual } } } -func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { +func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { if event.Guild.Unavailable { return @@ -144,7 +144,7 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { } } -func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { +func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { if event.GuildID == BridgeConf.GID { if event.ChannelID == BridgeConf.CID { //get user diff --git a/main.go b/main.go index b4d0fff..597de55 100644 --- a/main.go +++ b/main.go @@ -59,32 +59,13 @@ func main() { log.Println("Unable to set priority. ", err) } - // DISCORD Setup - + //Connect to discord discord, err := discordgo.New("Bot " + *discordToken) if err != nil { log.Println(err) return } - // Open Websocket - discord.LogLevel = 2 - discord.StateEnabled = true - discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) - // register handlers - discord.AddHandler(ready) - discord.AddHandler(messageCreate) - discord.AddHandler(guildCreate) - discord.AddHandler(voiceUpdate) - err = discord.Open() - if err != nil { - log.Println(err) - return - } - defer discord.Close() - - log.Println("Discord Bot Connected") - log.Printf("Discord bot looking for command !%v", *discordCommand) // Mumble setup config := gumble.NewConfig() config.Username = *mumbleUsername @@ -110,12 +91,33 @@ func main() { DiscordUsers: make(map[string]bool), } l := &Listener{BridgeConf, Bridge} + + // Discord setup + // Open Websocket + discord.LogLevel = 2 + discord.StateEnabled = true + discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) + // register handlers + discord.AddHandler(l.ready) + discord.AddHandler(l.messageCreate) + discord.AddHandler(l.guildCreate) + discord.AddHandler(l.voiceUpdate) + err = discord.Open() + if err != nil { + log.Println(err) + return + } + defer discord.Close() + + log.Println("Discord Bot Connected") + log.Printf("Discord bot looking for command !%v", *discordCommand) + switch *mode { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) BridgeConf.Mode = BridgeModeAuto - //go AutoBridge(discord,l) + go AutoBridge(discord, l) case "manual": log.Println("bridge starting in manual mode") BridgeConf.Mode = BridgeModeManual From db0af7edcd0b2a31eb2c880e8081d66d5d9b9221 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 20:25:38 -0500 Subject: [PATCH 25/37] remove global conf and state variables --- bridge.go | 47 ++++++++++++++++++++++++----------------------- handlers.go | 36 ++++++++++++++++++------------------ main.go | 9 +++------ 3 files changed, 45 insertions(+), 47 deletions(-) diff --git a/bridge.go b/bridge.go index b8a7caf..5c2522d 100644 --- a/bridge.go +++ b/bridge.go @@ -57,7 +57,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin return } defer mumble.Disconnect() - Bridge.Client = mumble + l.Bridge.Client = mumble // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 var toMumble = mumble.AudioOutgoing() @@ -109,18 +109,18 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } for _, vs := range g.VoiceStates { if vs.ChannelID == discordCID { - Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 u, err := discord.User(vs.UserID) if err != nil { log.Println("error looking up username") - Bridge.DiscordUsers[u.Username] = true - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + l.Bridge.DiscordUsers[u.Username] = true + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) }) } } } - Bridge.Connected = true + l.Bridge.Connected = true select { case sig := <-c: @@ -132,15 +132,16 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin close(die) close(m.Close) close(toMumble) - Bridge.Connected = false - Bridge.Client = nil - Bridge.MumbleUserCount = 0 - Bridge.DiscordUserCount = 0 - Bridge.DiscordUsers = make(map[string]bool) + l.Bridge.Connected = false + l.Bridge.Client = nil + l.Bridge.MumbleUserCount = 0 + l.Bridge.MumbleUsers = make(map[string]bool) + l.Bridge.DiscordUserCount = 0 + l.Bridge.DiscordUsers = make(map[string]bool) } } -func discordStatusUpdate(dg *discordgo.Session, host, port string) { +func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) { status := "" curr := 0 m, _ := time.ParseDuration("30s") @@ -153,17 +154,17 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string) { dg.UpdateListeningStatus("an error pinging mumble") } else { curr = resp.ConnectedUsers - if Bridge.Connected { + if l.Bridge.Connected { curr = curr - 1 } - if curr != Bridge.MumbleUserCount { - Bridge.MumbleUserCount = curr + if curr != l.Bridge.MumbleUserCount { + l.Bridge.MumbleUserCount = curr } if curr == 0 { status = "" } else { - if len(Bridge.MumbleUsers) > 0 { - status = fmt.Sprintf("%v/%v users in Mumble\n", len(Bridge.MumbleUsers), curr) + if len(l.Bridge.MumbleUsers) > 0 { + status = fmt.Sprintf("%v/%v users in Mumble\n", len(l.Bridge.MumbleUsers), curr) } else { status = fmt.Sprintf("%v users in Mumble\n", curr) } @@ -178,20 +179,20 @@ func AutoBridge(s *discordgo.Session, l *Listener) { for { select { default: - case <-Bridge.AutoChan: + case <-l.Bridge.AutoChan: log.Println("ending automode") return } time.Sleep(3 * time.Second) - if !Bridge.Connected && Bridge.MumbleUserCount > 0 && Bridge.DiscordUserCount > 0 { + if !l.Bridge.Connected && l.Bridge.MumbleUserCount > 0 && l.Bridge.DiscordUserCount > 0 { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) - Bridge.ActiveConn = die - go startBridge(s, BridgeConf.GID, BridgeConf.CID, l, die) + l.Bridge.ActiveConn = die + go startBridge(s, l.BridgeConf.GID, l.BridgeConf.CID, l, die) } - if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { + if l.Bridge.Connected && l.Bridge.MumbleUserCount == 0 && l.Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") - Bridge.ActiveConn <- true + l.Bridge.ActiveConn <- true MumbleReset() DiscordReset() } diff --git a/handlers.go b/handlers.go index 2d0b4b1..d84df61 100644 --- a/handlers.go +++ b/handlers.go @@ -145,27 +145,27 @@ func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreat } func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { - if event.GuildID == BridgeConf.GID { - if event.ChannelID == BridgeConf.CID { + if event.GuildID == l.BridgeConf.GID { + if event.ChannelID == l.BridgeConf.CID { //get user u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) } //check to see if actually new user - if Bridge.DiscordUsers[u.Username] { + if l.Bridge.DiscordUsers[u.Username] { //not actually new user return } log.Println("user joined watched discord channel") - if Bridge.Connected { - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) }) } - Bridge.DiscordUsers[u.Username] = true - log.Println(Bridge.DiscordUsers) - Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + l.Bridge.DiscordUsers[u.Username] = true + log.Println(l.Bridge.DiscordUsers) + l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 } if event.ChannelID == "" { //leave event, trigger recount of active users @@ -179,23 +179,23 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState // Look for current voice states in watched channel count := 0 for _, vs := range g.VoiceStates { - if vs.ChannelID == BridgeConf.CID { + if vs.ChannelID == l.BridgeConf.CID { count = count + 1 } } - if Bridge.DiscordUserCount > count { + if l.Bridge.DiscordUserCount > count { u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) } - delete(Bridge.DiscordUsers, u.Username) + delete(l.Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") - if Bridge.Connected { - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) }) } - Bridge.DiscordUserCount = count + l.Bridge.DiscordUserCount = count } } @@ -206,7 +206,7 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { if l.BridgeConf.MumbleChannel != "" { //join specified channel - startingChannel := e.Client.Channels.Find(BridgeConf.MumbleChannel) + startingChannel := e.Client.Channels.Find(l.BridgeConf.MumbleChannel) if startingChannel != nil { e.Client.Self.Move(startingChannel) } @@ -215,7 +215,7 @@ func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { - Bridge.MumbleUsers = make(map[string]bool) + l.Bridge.MumbleUsers = make(map[string]bool) for _, user := range l.Bridge.Client.Self.Channel.Users { //note, this might be too slow for really really big channels? //event listeners block while processing diff --git a/main.go b/main.go index 597de55..6687c72 100644 --- a/main.go +++ b/main.go @@ -15,9 +15,6 @@ import ( _ "layeh.com/gumble/opus" ) -var BridgeConf *BridgeConfig -var Bridge *BridgeState - func main() { godotenv.Load() @@ -73,7 +70,7 @@ func main() { config.AudioInterval = time.Millisecond * 10 // Bridge setup - BridgeConf = &BridgeConfig{ + BridgeConf := &BridgeConfig{ Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, @@ -83,7 +80,7 @@ func main() { GID: *discordGID, CID: *discordCID, } - Bridge = &BridgeState{ + Bridge := &BridgeState{ ActiveConn: make(chan bool), Connected: false, MumbleUserCount: 0, @@ -130,7 +127,7 @@ func main() { log.Fatalln("invalid bridge mode set") } - go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) + go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort), l) sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc From 645c7676c14c152c2fbe8d050f723af309ce1ebf Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 20:33:40 -0500 Subject: [PATCH 26/37] remove extra debug code --- handlers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/handlers.go b/handlers.go index 256108f..ab7186a 100644 --- a/handlers.go +++ b/handlers.go @@ -159,7 +159,6 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { }) } Bridge.DiscordUsers[u.Username] = true - log.Println(Bridge.DiscordUsers) Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 } if event.ChannelID == "" { From ab1046838c65d2bb8b46e2363e708b53ada177c2 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 21:56:37 -0500 Subject: [PATCH 27/37] integrate mutex fix --- discord.go | 2 ++ main.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/discord.go b/discord.go index 77e2233..462abb7 100644 --- a/discord.go +++ b/discord.go @@ -183,6 +183,8 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) { case fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480]: default: log.Println("fromDiscordMap buffer full. Dropping packet") + discordMutex.Unlock() + continue } select { case fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960]: diff --git a/main.go b/main.go index 10dad71..21e2666 100644 --- a/main.go +++ b/main.go @@ -54,10 +54,10 @@ func main() { if *mode == "" { log.Fatalln("missing mode set") } - err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) - if err != nil { - log.Println("Unable to set priority. ", err) - } + //err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) + //if err != nil { + // log.Println("Unable to set priority. ", err) + //} // DISCORD Setup From 8e2fe4f3b727326ebee4dd0cbbc046c3bd1dffd4 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 21:58:38 -0500 Subject: [PATCH 28/37] actually add users in initial run --- bridge.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bridge.go b/bridge.go index 48a84b8..a38722a 100644 --- a/bridge.go +++ b/bridge.go @@ -113,11 +113,12 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin u, err := discord.User(vs.UserID) if err != nil { log.Println("error looking up username") - Bridge.DiscordUsers[u.Username] = true - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) - }) + continue } + Bridge.DiscordUsers[u.Username] = true + Bridge.Client.Do(func() { + Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + }) } } Bridge.Connected = true From f4cc9498cb4a5da585e51e9c7b8ad41acc4de831 Mon Sep 17 00:00:00 2001 From: Steve Date: Wed, 6 Jan 2021 19:12:56 -0500 Subject: [PATCH 29/37] add mutexs to prevent data races, re-organize a bit --- bridge.go | 35 ++++++++++------------------------- config.go | 1 - handlers.go | 50 ++++++++++++++++++++++++++++++++++++++++++++------ main.go | 18 +++++++++++++----- 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/bridge.go b/bridge.go index 5c2522d..3b1b4d0 100644 --- a/bridge.go +++ b/bridge.go @@ -12,12 +12,12 @@ import ( "github.com/bwmarrin/discordgo" "layeh.com/gumble/gumble" - "layeh.com/gumble/gumbleutil" ) type BridgeState struct { ActiveConn chan bool Connected bool + Mode BridgeMode Client *gumble.Client DiscordUsers map[string]bool MumbleUsers map[string]bool @@ -46,10 +46,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if l.BridgeConf.MumbleInsecure { tlsConfig.InsecureSkipVerify = true } - l.BridgeConf.Config.Attach(gumbleutil.Listener{ - Connect: l.mumbleConnect, - UserChange: l.mumbleUserChange, - }) + mumble, err := gumble.DialWithDialer(new(net.Dialer), l.BridgeConf.MumbleAddr, l.BridgeConf.Config, &tlsConfig) if err != nil { @@ -100,27 +97,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } } }() - - //Setup initial discord state - g, err := discord.State.Guild(discordGID) - if err != nil { - log.Println("error finding guild") - panic(err) - } - for _, vs := range g.VoiceStates { - if vs.ChannelID == discordCID { - l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 - u, err := discord.User(vs.UserID) - if err != nil { - log.Println("error looking up username") - l.Bridge.DiscordUsers[u.Username] = true - l.Bridge.Client.Do(func() { - l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) - }) - } - } - } + l.ConnectedLock.Lock() l.Bridge.Connected = true + l.ConnectedLock.Unlock() select { case sig := <-c: @@ -153,6 +132,8 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) log.Printf("error pinging mumble server %v\n", err) dg.UpdateListeningStatus("an error pinging mumble") } else { + l.UserCountLock.Lock() + l.ConnectedLock.Lock() curr = resp.ConnectedUsers if l.Bridge.Connected { curr = curr - 1 @@ -169,6 +150,8 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) status = fmt.Sprintf("%v users in Mumble\n", curr) } } + l.ConnectedLock.Unlock() + l.UserCountLock.Unlock() dg.UpdateListeningStatus(status) } } @@ -184,6 +167,7 @@ func AutoBridge(s *discordgo.Session, l *Listener) { return } time.Sleep(3 * time.Second) + l.UserCountLock.Lock() if !l.Bridge.Connected && l.Bridge.MumbleUserCount > 0 && l.Bridge.DiscordUserCount > 0 { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) @@ -196,5 +180,6 @@ func AutoBridge(s *discordgo.Session, l *Listener) { MumbleReset() DiscordReset() } + l.UserCountLock.Unlock() } } diff --git a/config.go b/config.go index 5fd9885..cf58b82 100644 --- a/config.go +++ b/config.go @@ -23,7 +23,6 @@ type BridgeConfig struct { MumbleAddr string MumbleInsecure bool MumbleChannel string - Mode BridgeMode Command string GID string CID string diff --git a/handlers.go b/handlers.go index d84df61..d7c72c0 100644 --- a/handlers.go +++ b/handlers.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "strings" + "sync" "time" "github.com/bwmarrin/discordgo" @@ -11,17 +12,43 @@ import ( ) type Listener struct { - BridgeConf *BridgeConfig - Bridge *BridgeState + BridgeConf *BridgeConfig + Bridge *BridgeState + UserCountLock *sync.Mutex + ConnectedLock *sync.Mutex } func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") + //Setup initial discord state + var g *discordgo.Guild + for _, i := range event.Guilds { + if i.ID == l.BridgeConf.GID { + g = i + } + } + for _, vs := range g.VoiceStates { + if vs.ChannelID == l.BridgeConf.CID { + l.UserCountLock.Lock() + l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 + u, err := s.User(vs.UserID) + if err != nil { + log.Println("error looking up username") + } + l.Bridge.DiscordUsers[u.Username] = true + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + }) + } + l.UserCountLock.Unlock() + } + } } func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - if l.BridgeConf.Mode == BridgeModeConstant { + if l.Bridge.Mode == BridgeModeConstant { return } @@ -119,13 +146,13 @@ func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat } if strings.HasPrefix(m.Content, prefix+" auto") { - if l.BridgeConf.Mode != BridgeModeAuto { - l.BridgeConf.Mode = BridgeModeAuto + if l.Bridge.Mode != BridgeModeAuto { + l.Bridge.Mode = BridgeModeAuto l.Bridge.AutoChan = make(chan bool) go AutoBridge(s, l) } else { l.Bridge.AutoChan <- true - l.BridgeConf.Mode = BridgeModeManual + l.Bridge.Mode = BridgeModeManual } } } @@ -145,6 +172,7 @@ func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreat } func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { + l.UserCountLock.Lock() if event.GuildID == l.BridgeConf.GID { if event.ChannelID == l.BridgeConf.CID { //get user @@ -155,17 +183,21 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState //check to see if actually new user if l.Bridge.DiscordUsers[u.Username] { //not actually new user + l.UserCountLock.Unlock() return } log.Println("user joined watched discord channel") + l.ConnectedLock.Lock() if l.Bridge.Connected { l.Bridge.Client.Do(func() { l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) }) } + l.ConnectedLock.Unlock() l.Bridge.DiscordUsers[u.Username] = true log.Println(l.Bridge.DiscordUsers) l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 + l.UserCountLock.Unlock() } if event.ChannelID == "" { //leave event, trigger recount of active users @@ -173,6 +205,7 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState g, err := s.State.Guild(event.GuildID) if err != nil { // Could not find guild. + l.UserCountLock.Unlock() return } @@ -190,12 +223,15 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState } delete(l.Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") + l.ConnectedLock.Lock() if l.Bridge.Connected { l.Bridge.Client.Do(func() { l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) }) } + l.ConnectedLock.Unlock() l.Bridge.DiscordUserCount = count + l.UserCountLock.Unlock() } } @@ -214,6 +250,7 @@ func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { } func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { + l.UserCountLock.Lock() if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { l.Bridge.MumbleUsers = make(map[string]bool) for _, user := range l.Bridge.Client.Self.Channel.Users { @@ -225,4 +262,5 @@ func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { } } } + l.UserCountLock.Unlock() } diff --git a/main.go b/main.go index 6687c72..36e10d2 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,14 @@ import ( "os" "os/signal" "strconv" + "sync" "syscall" "time" "github.com/bwmarrin/discordgo" "github.com/joho/godotenv" "layeh.com/gumble/gumble" + "layeh.com/gumble/gumbleutil" _ "layeh.com/gumble/opus" ) @@ -75,7 +77,6 @@ func main() { MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, MumbleChannel: *mumbleChannel, - Mode: -1, Command: *discordCommand, GID: *discordGID, CID: *discordCID, @@ -86,8 +87,11 @@ func main() { MumbleUserCount: 0, DiscordUserCount: 0, DiscordUsers: make(map[string]bool), + MumbleUsers: make(map[string]bool), } - l := &Listener{BridgeConf, Bridge} + ul := &sync.Mutex{} + cl := &sync.Mutex{} + l := &Listener{BridgeConf, Bridge, ul, cl} // Discord setup // Open Websocket @@ -100,6 +104,10 @@ func main() { discord.AddHandler(l.guildCreate) discord.AddHandler(l.voiceUpdate) err = discord.Open() + l.BridgeConf.Config.Attach(gumbleutil.Listener{ + Connect: l.mumbleConnect, + UserChange: l.mumbleUserChange, + }) if err != nil { log.Println(err) return @@ -113,14 +121,14 @@ func main() { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) - BridgeConf.Mode = BridgeModeAuto + Bridge.Mode = BridgeModeAuto go AutoBridge(discord, l) case "manual": log.Println("bridge starting in manual mode") - BridgeConf.Mode = BridgeModeManual + Bridge.Mode = BridgeModeManual case "constant": log.Println("bridge starting in constant mode") - BridgeConf.Mode = BridgeModeConstant + Bridge.Mode = BridgeModeConstant go startBridge(discord, *discordGID, *discordCID, l, make(chan bool)) default: discord.Close() From 83712bfe156612e6e0887a10c87671cf0b0fc0e3 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 19:43:55 -0500 Subject: [PATCH 30/37] mumble event listeners use non-global state --- bridge.go | 18 +++++++++--------- handlers.go | 23 ++++++++++++++--------- main.go | 5 +++-- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/bridge.go b/bridge.go index a38722a..e9de085 100644 --- a/bridge.go +++ b/bridge.go @@ -26,7 +26,7 @@ type BridgeState struct { AutoChan chan bool } -func startBridge(discord *discordgo.Session, discordGID string, discordCID string, config *gumble.Config, mumbleAddr string, mumbleInsecure bool, die chan bool) { +func startBridge(discord *discordgo.Session, discordGID string, discordCID string, l *Listener, die chan bool) { dgv, err := discord.ChannelVoiceJoin(discordGID, discordCID, false, false) if err != nil { log.Println(err) @@ -43,14 +43,14 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } var tlsConfig tls.Config - if mumbleInsecure { + if l.BridgeConf.MumbleInsecure { tlsConfig.InsecureSkipVerify = true } - config.Attach(gumbleutil.Listener{ - Connect: mumbleConnect, - UserChange: mumbleUserChange, + l.BridgeConf.Config.Attach(gumbleutil.Listener{ + Connect: l.mumbleConnect, + UserChange: l.mumbleUserChange, }) - mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig) + mumble, err := gumble.DialWithDialer(new(net.Dialer), l.BridgeConf.MumbleAddr, l.BridgeConf.Config, &tlsConfig) if err != nil { log.Println(err) @@ -68,7 +68,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin // Start Passing Between // Mumble go m.fromMumbleMixer(toDiscord, die) - det := config.AudioListeners.Attach(m) + det := l.BridgeConf.Config.AudioListeners.Attach(m) //Discord go discordReceivePCM(dgv, die) @@ -174,7 +174,7 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string) { } } -func AutoBridge(s *discordgo.Session) { +func AutoBridge(s *discordgo.Session, l *Listener) { log.Println("beginning auto mode") for { select { @@ -188,7 +188,7 @@ func AutoBridge(s *discordgo.Session) { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) Bridge.ActiveConn = die - go startBridge(s, BridgeConf.GID, BridgeConf.CID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + go startBridge(s, BridgeConf.GID, BridgeConf.CID, l, die) } if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") diff --git a/handlers.go b/handlers.go index ab7186a..8d8649f 100644 --- a/handlers.go +++ b/handlers.go @@ -10,6 +10,11 @@ import ( "layeh.com/gumble/gumble" ) +type Listener struct { + BridgeConf *BridgeConfig + Bridge *BridgeState +} + func ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") } @@ -47,7 +52,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) die := make(chan bool) Bridge.ActiveConn = die - go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) return } } @@ -107,7 +112,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { DiscordReset() time.Sleep(5 * time.Second) Bridge.ActiveConn = make(chan bool) - go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) + //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) return } } @@ -117,7 +122,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if BridgeConf.Mode != BridgeModeAuto { BridgeConf.Mode = BridgeModeAuto Bridge.AutoChan = make(chan bool) - go AutoBridge(s) + //go AutoBridge(s) } else { Bridge.AutoChan <- true BridgeConf.Mode = BridgeModeManual @@ -197,8 +202,8 @@ func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { return } -func mumbleConnect(e *gumble.ConnectEvent) { - if BridgeConf.MumbleChannel != "" { +func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { + if l.BridgeConf.MumbleChannel != "" { //join specified channel startingChannel := e.Client.Channels.Find(BridgeConf.MumbleChannel) if startingChannel != nil { @@ -207,15 +212,15 @@ func mumbleConnect(e *gumble.ConnectEvent) { } } -func mumbleUserChange(e *gumble.UserChangeEvent) { +func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { Bridge.MumbleUsers = make(map[string]bool) - for _, user := range Bridge.Client.Self.Channel.Users { + for _, user := range l.Bridge.Client.Self.Channel.Users { //note, this might be too slow for really really big channels? //event listeners block while processing //also probably bad to rebuild the set every user change. - if user.Name != Bridge.Client.Self.Name { - Bridge.MumbleUsers[user.Name] = true + if user.Name != l.Bridge.Client.Self.Name { + l.Bridge.MumbleUsers[user.Name] = true } } } diff --git a/main.go b/main.go index 21e2666..9d5e076 100644 --- a/main.go +++ b/main.go @@ -109,19 +109,20 @@ func main() { DiscordUserCount: 0, DiscordUsers: make(map[string]bool), } + l := &Listener{BridgeConf, Bridge} switch *mode { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) BridgeConf.Mode = BridgeModeAuto - go AutoBridge(discord) + //go AutoBridge(discord,l) case "manual": log.Println("bridge starting in manual mode") BridgeConf.Mode = BridgeModeManual case "constant": log.Println("bridge starting in constant mode") BridgeConf.Mode = BridgeModeConstant - go startBridge(discord, *discordGID, *discordCID, config, BridgeConf.MumbleAddr, *mumbleInsecure, make(chan bool)) + go startBridge(discord, *discordGID, *discordCID, l, make(chan bool)) default: discord.Close() log.Fatalln("invalid bridge mode set") From ef8af898938db5698dfc1d1772acd65db60297b2 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 20:21:08 -0500 Subject: [PATCH 31/37] discord event listeners use non-global state --- handlers.go | 38 +++++++++++++++++++------------------- main.go | 44 +++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/handlers.go b/handlers.go index 8d8649f..5bade39 100644 --- a/handlers.go +++ b/handlers.go @@ -15,13 +15,13 @@ type Listener struct { Bridge *BridgeState } -func ready(s *discordgo.Session, event *discordgo.Ready) { +func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") } -func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { +func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - if BridgeConf.Mode == BridgeModeConstant { + if l.BridgeConf.Mode == BridgeModeConstant { return } @@ -29,7 +29,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if m.Author.ID == s.State.User.ID { return } - prefix := "!" + BridgeConf.Command + prefix := "!" + l.BridgeConf.Command if strings.HasPrefix(m.Content, prefix+" link") { // Find the channel that the message came from. @@ -51,8 +51,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if vs.UserID == m.Author.ID { log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) die := make(chan bool) - Bridge.ActiveConn = die - //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die) + l.Bridge.ActiveConn = die + go startBridge(s, g.ID, vs.ChannelID, l, die) return } } @@ -78,8 +78,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) - Bridge.ActiveConn <- true - Bridge.ActiveConn = nil + l.Bridge.ActiveConn <- true + l.Bridge.ActiveConn = nil MumbleReset() DiscordReset() return @@ -107,30 +107,30 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) - Bridge.ActiveConn <- true + l.Bridge.ActiveConn <- true MumbleReset() DiscordReset() time.Sleep(5 * time.Second) - Bridge.ActiveConn = make(chan bool) - //go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn) + l.Bridge.ActiveConn = make(chan bool) + go startBridge(s, g.ID, vs.ChannelID, l, l.Bridge.ActiveConn) return } } } if strings.HasPrefix(m.Content, prefix+" auto") { - if BridgeConf.Mode != BridgeModeAuto { - BridgeConf.Mode = BridgeModeAuto - Bridge.AutoChan = make(chan bool) - //go AutoBridge(s) + if l.BridgeConf.Mode != BridgeModeAuto { + l.BridgeConf.Mode = BridgeModeAuto + l.Bridge.AutoChan = make(chan bool) + go AutoBridge(s, l) } else { - Bridge.AutoChan <- true - BridgeConf.Mode = BridgeModeManual + l.Bridge.AutoChan <- true + l.BridgeConf.Mode = BridgeModeManual } } } -func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { +func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { if event.Guild.Unavailable { return @@ -144,7 +144,7 @@ func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { } } -func voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { +func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { if event.GuildID == BridgeConf.GID { if event.ChannelID == BridgeConf.CID { //get user diff --git a/main.go b/main.go index 9d5e076..4ce02e9 100644 --- a/main.go +++ b/main.go @@ -59,32 +59,13 @@ func main() { // log.Println("Unable to set priority. ", err) //} - // DISCORD Setup - + //Connect to discord discord, err := discordgo.New("Bot " + *discordToken) if err != nil { log.Println(err) return } - // Open Websocket - discord.LogLevel = 2 - discord.StateEnabled = true - discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) - // register handlers - discord.AddHandler(ready) - discord.AddHandler(messageCreate) - discord.AddHandler(guildCreate) - discord.AddHandler(voiceUpdate) - err = discord.Open() - if err != nil { - log.Println(err) - return - } - defer discord.Close() - - log.Println("Discord Bot Connected") - log.Printf("Discord bot looking for command !%v", *discordCommand) // Mumble setup config := gumble.NewConfig() config.Username = *mumbleUsername @@ -110,12 +91,33 @@ func main() { DiscordUsers: make(map[string]bool), } l := &Listener{BridgeConf, Bridge} + + // Discord setup + // Open Websocket + discord.LogLevel = 2 + discord.StateEnabled = true + discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged) + // register handlers + discord.AddHandler(l.ready) + discord.AddHandler(l.messageCreate) + discord.AddHandler(l.guildCreate) + discord.AddHandler(l.voiceUpdate) + err = discord.Open() + if err != nil { + log.Println(err) + return + } + defer discord.Close() + + log.Println("Discord Bot Connected") + log.Printf("Discord bot looking for command !%v", *discordCommand) + switch *mode { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) BridgeConf.Mode = BridgeModeAuto - //go AutoBridge(discord,l) + go AutoBridge(discord, l) case "manual": log.Println("bridge starting in manual mode") BridgeConf.Mode = BridgeModeManual From 301bc1b3d1db03cf11eecba2241ee702550cbf15 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 5 Jan 2021 20:25:38 -0500 Subject: [PATCH 32/37] remove global conf and state variables --- bridge.go | 59 ++++++++++++++++++----------------------------------- handlers.go | 34 +++++++++++++++--------------- main.go | 9 +++----- 3 files changed, 40 insertions(+), 62 deletions(-) diff --git a/bridge.go b/bridge.go index e9de085..2962f2e 100644 --- a/bridge.go +++ b/bridge.go @@ -57,7 +57,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin return } defer mumble.Disconnect() - Bridge.Client = mumble + l.Bridge.Client = mumble // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 var toMumble = mumble.AudioOutgoing() @@ -101,27 +101,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } }() - //Setup initial discord state - g, err := discord.State.Guild(discordGID) - if err != nil { - log.Println("error finding guild") - panic(err) - } - for _, vs := range g.VoiceStates { - if vs.ChannelID == discordCID { - Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 - u, err := discord.User(vs.UserID) - if err != nil { - log.Println("error looking up username") - continue - } - Bridge.DiscordUsers[u.Username] = true - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) - }) - } - } - Bridge.Connected = true + l.Bridge.Connected = true select { case sig := <-c: @@ -133,15 +113,16 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin close(die) close(m.Close) close(toMumble) - Bridge.Connected = false - Bridge.Client = nil - Bridge.MumbleUserCount = 0 - Bridge.DiscordUserCount = 0 - Bridge.DiscordUsers = make(map[string]bool) + l.Bridge.Connected = false + l.Bridge.Client = nil + l.Bridge.MumbleUserCount = 0 + l.Bridge.MumbleUsers = make(map[string]bool) + l.Bridge.DiscordUserCount = 0 + l.Bridge.DiscordUsers = make(map[string]bool) } } -func discordStatusUpdate(dg *discordgo.Session, host, port string) { +func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) { status := "" curr := 0 m, _ := time.ParseDuration("30s") @@ -154,17 +135,17 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string) { dg.UpdateListeningStatus("an error pinging mumble") } else { curr = resp.ConnectedUsers - if Bridge.Connected { + if l.Bridge.Connected { curr = curr - 1 } - if curr != Bridge.MumbleUserCount { - Bridge.MumbleUserCount = curr + if curr != l.Bridge.MumbleUserCount { + l.Bridge.MumbleUserCount = curr } if curr == 0 { status = "" } else { - if len(Bridge.MumbleUsers) > 0 { - status = fmt.Sprintf("%v/%v users in Mumble\n", len(Bridge.MumbleUsers), curr) + if len(l.Bridge.MumbleUsers) > 0 { + status = fmt.Sprintf("%v/%v users in Mumble\n", len(l.Bridge.MumbleUsers), curr) } else { status = fmt.Sprintf("%v users in Mumble\n", curr) } @@ -179,20 +160,20 @@ func AutoBridge(s *discordgo.Session, l *Listener) { for { select { default: - case <-Bridge.AutoChan: + case <-l.Bridge.AutoChan: log.Println("ending automode") return } time.Sleep(3 * time.Second) - if !Bridge.Connected && Bridge.MumbleUserCount > 0 && Bridge.DiscordUserCount > 0 { + if !l.Bridge.Connected && l.Bridge.MumbleUserCount > 0 && l.Bridge.DiscordUserCount > 0 { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) - Bridge.ActiveConn = die - go startBridge(s, BridgeConf.GID, BridgeConf.CID, l, die) + l.Bridge.ActiveConn = die + go startBridge(s, l.BridgeConf.GID, l.BridgeConf.CID, l, die) } - if Bridge.Connected && Bridge.MumbleUserCount == 0 && Bridge.DiscordUserCount <= 1 { + if l.Bridge.Connected && l.Bridge.MumbleUserCount == 0 && l.Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") - Bridge.ActiveConn <- true + l.Bridge.ActiveConn <- true MumbleReset() DiscordReset() } diff --git a/handlers.go b/handlers.go index 5bade39..9a1a287 100644 --- a/handlers.go +++ b/handlers.go @@ -145,26 +145,26 @@ func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreat } func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { - if event.GuildID == BridgeConf.GID { - if event.ChannelID == BridgeConf.CID { + if event.GuildID == l.BridgeConf.GID { + if event.ChannelID == l.BridgeConf.CID { //get user u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) } //check to see if actually new user - if Bridge.DiscordUsers[u.Username] { + if l.Bridge.DiscordUsers[u.Username] { //not actually new user return } log.Println("user joined watched discord channel") - if Bridge.Connected { - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) }) } - Bridge.DiscordUsers[u.Username] = true - Bridge.DiscordUserCount = Bridge.DiscordUserCount + 1 + l.Bridge.DiscordUsers[u.Username] = true + l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 } if event.ChannelID == "" { //leave event, trigger recount of active users @@ -178,23 +178,23 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState // Look for current voice states in watched channel count := 0 for _, vs := range g.VoiceStates { - if vs.ChannelID == BridgeConf.CID { + if vs.ChannelID == l.BridgeConf.CID { count = count + 1 } } - if Bridge.DiscordUserCount > count { + if l.Bridge.DiscordUserCount > count { u, err := s.User(event.UserID) if err != nil { log.Printf("error looking up user for uid %v", event.UserID) } - delete(Bridge.DiscordUsers, u.Username) + delete(l.Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") - if Bridge.Connected { - Bridge.Client.Do(func() { - Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) }) } - Bridge.DiscordUserCount = count + l.Bridge.DiscordUserCount = count } } @@ -205,7 +205,7 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { if l.BridgeConf.MumbleChannel != "" { //join specified channel - startingChannel := e.Client.Channels.Find(BridgeConf.MumbleChannel) + startingChannel := e.Client.Channels.Find(l.BridgeConf.MumbleChannel) if startingChannel != nil { e.Client.Self.Move(startingChannel) } @@ -214,7 +214,7 @@ func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { - Bridge.MumbleUsers = make(map[string]bool) + l.Bridge.MumbleUsers = make(map[string]bool) for _, user := range l.Bridge.Client.Self.Channel.Users { //note, this might be too slow for really really big channels? //event listeners block while processing diff --git a/main.go b/main.go index 4ce02e9..9416024 100644 --- a/main.go +++ b/main.go @@ -15,9 +15,6 @@ import ( _ "layeh.com/gumble/opus" ) -var BridgeConf *BridgeConfig -var Bridge *BridgeState - func main() { godotenv.Load() @@ -73,7 +70,7 @@ func main() { config.AudioInterval = time.Millisecond * 10 // Bridge setup - BridgeConf = &BridgeConfig{ + BridgeConf := &BridgeConfig{ Config: config, MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, @@ -83,7 +80,7 @@ func main() { GID: *discordGID, CID: *discordCID, } - Bridge = &BridgeState{ + Bridge := &BridgeState{ ActiveConn: make(chan bool), Connected: false, MumbleUserCount: 0, @@ -130,7 +127,7 @@ func main() { log.Fatalln("invalid bridge mode set") } - go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort)) + go discordStatusUpdate(discord, *mumbleAddr, strconv.Itoa(*mumblePort), l) sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc From 2f4bf65d20b5c233a62a50da26f7793298110a4b Mon Sep 17 00:00:00 2001 From: Steve Date: Wed, 6 Jan 2021 19:12:56 -0500 Subject: [PATCH 33/37] add mutexs to prevent data races, re-organize a bit --- bridge.go | 16 ++++++++++------ config.go | 1 - handlers.go | 50 ++++++++++++++++++++++++++++++++++++++++++++------ main.go | 18 +++++++++++++----- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/bridge.go b/bridge.go index 2962f2e..3b1b4d0 100644 --- a/bridge.go +++ b/bridge.go @@ -12,12 +12,12 @@ import ( "github.com/bwmarrin/discordgo" "layeh.com/gumble/gumble" - "layeh.com/gumble/gumbleutil" ) type BridgeState struct { ActiveConn chan bool Connected bool + Mode BridgeMode Client *gumble.Client DiscordUsers map[string]bool MumbleUsers map[string]bool @@ -46,10 +46,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin if l.BridgeConf.MumbleInsecure { tlsConfig.InsecureSkipVerify = true } - l.BridgeConf.Config.Attach(gumbleutil.Listener{ - Connect: l.mumbleConnect, - UserChange: l.mumbleUserChange, - }) + mumble, err := gumble.DialWithDialer(new(net.Dialer), l.BridgeConf.MumbleAddr, l.BridgeConf.Config, &tlsConfig) if err != nil { @@ -100,8 +97,9 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin } } }() - + l.ConnectedLock.Lock() l.Bridge.Connected = true + l.ConnectedLock.Unlock() select { case sig := <-c: @@ -134,6 +132,8 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) log.Printf("error pinging mumble server %v\n", err) dg.UpdateListeningStatus("an error pinging mumble") } else { + l.UserCountLock.Lock() + l.ConnectedLock.Lock() curr = resp.ConnectedUsers if l.Bridge.Connected { curr = curr - 1 @@ -150,6 +150,8 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) status = fmt.Sprintf("%v users in Mumble\n", curr) } } + l.ConnectedLock.Unlock() + l.UserCountLock.Unlock() dg.UpdateListeningStatus(status) } } @@ -165,6 +167,7 @@ func AutoBridge(s *discordgo.Session, l *Listener) { return } time.Sleep(3 * time.Second) + l.UserCountLock.Lock() if !l.Bridge.Connected && l.Bridge.MumbleUserCount > 0 && l.Bridge.DiscordUserCount > 0 { log.Println("users detected in mumble and discord, bridging") die := make(chan bool) @@ -177,5 +180,6 @@ func AutoBridge(s *discordgo.Session, l *Listener) { MumbleReset() DiscordReset() } + l.UserCountLock.Unlock() } } diff --git a/config.go b/config.go index 5fd9885..cf58b82 100644 --- a/config.go +++ b/config.go @@ -23,7 +23,6 @@ type BridgeConfig struct { MumbleAddr string MumbleInsecure bool MumbleChannel string - Mode BridgeMode Command string GID string CID string diff --git a/handlers.go b/handlers.go index 9a1a287..a22101f 100644 --- a/handlers.go +++ b/handlers.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "strings" + "sync" "time" "github.com/bwmarrin/discordgo" @@ -11,17 +12,43 @@ import ( ) type Listener struct { - BridgeConf *BridgeConfig - Bridge *BridgeState + BridgeConf *BridgeConfig + Bridge *BridgeState + UserCountLock *sync.Mutex + ConnectedLock *sync.Mutex } func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") + //Setup initial discord state + var g *discordgo.Guild + for _, i := range event.Guilds { + if i.ID == l.BridgeConf.GID { + g = i + } + } + for _, vs := range g.VoiceStates { + if vs.ChannelID == l.BridgeConf.CID { + l.UserCountLock.Lock() + l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 + u, err := s.User(vs.UserID) + if err != nil { + log.Println("error looking up username") + } + l.Bridge.DiscordUsers[u.Username] = true + if l.Bridge.Connected { + l.Bridge.Client.Do(func() { + l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) + }) + } + l.UserCountLock.Unlock() + } + } } func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - if l.BridgeConf.Mode == BridgeModeConstant { + if l.Bridge.Mode == BridgeModeConstant { return } @@ -119,13 +146,13 @@ func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat } if strings.HasPrefix(m.Content, prefix+" auto") { - if l.BridgeConf.Mode != BridgeModeAuto { - l.BridgeConf.Mode = BridgeModeAuto + if l.Bridge.Mode != BridgeModeAuto { + l.Bridge.Mode = BridgeModeAuto l.Bridge.AutoChan = make(chan bool) go AutoBridge(s, l) } else { l.Bridge.AutoChan <- true - l.BridgeConf.Mode = BridgeModeManual + l.Bridge.Mode = BridgeModeManual } } } @@ -145,6 +172,7 @@ func (l *Listener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreat } func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) { + l.UserCountLock.Lock() if event.GuildID == l.BridgeConf.GID { if event.ChannelID == l.BridgeConf.CID { //get user @@ -155,16 +183,20 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState //check to see if actually new user if l.Bridge.DiscordUsers[u.Username] { //not actually new user + l.UserCountLock.Unlock() return } log.Println("user joined watched discord channel") + l.ConnectedLock.Lock() if l.Bridge.Connected { l.Bridge.Client.Do(func() { l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has joined Discord channel\n", u.Username), false) }) } + l.ConnectedLock.Unlock() l.Bridge.DiscordUsers[u.Username] = true l.Bridge.DiscordUserCount = l.Bridge.DiscordUserCount + 1 + l.UserCountLock.Unlock() } if event.ChannelID == "" { //leave event, trigger recount of active users @@ -172,6 +204,7 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState g, err := s.State.Guild(event.GuildID) if err != nil { // Could not find guild. + l.UserCountLock.Unlock() return } @@ -189,12 +222,15 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState } delete(l.Bridge.DiscordUsers, u.Username) log.Println("user left watched discord channel") + l.ConnectedLock.Lock() if l.Bridge.Connected { l.Bridge.Client.Do(func() { l.Bridge.Client.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", u.Username), false) }) } + l.ConnectedLock.Unlock() l.Bridge.DiscordUserCount = count + l.UserCountLock.Unlock() } } @@ -213,6 +249,7 @@ func (l *Listener) mumbleConnect(e *gumble.ConnectEvent) { } func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { + l.UserCountLock.Lock() if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) { l.Bridge.MumbleUsers = make(map[string]bool) for _, user := range l.Bridge.Client.Self.Channel.Users { @@ -224,4 +261,5 @@ func (l *Listener) mumbleUserChange(e *gumble.UserChangeEvent) { } } } + l.UserCountLock.Unlock() } diff --git a/main.go b/main.go index 9416024..77ce55b 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,14 @@ import ( "os" "os/signal" "strconv" + "sync" "syscall" "time" "github.com/bwmarrin/discordgo" "github.com/joho/godotenv" "layeh.com/gumble/gumble" + "layeh.com/gumble/gumbleutil" _ "layeh.com/gumble/opus" ) @@ -75,7 +77,6 @@ func main() { MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleInsecure: *mumbleInsecure, MumbleChannel: *mumbleChannel, - Mode: -1, Command: *discordCommand, GID: *discordGID, CID: *discordCID, @@ -86,8 +87,11 @@ func main() { MumbleUserCount: 0, DiscordUserCount: 0, DiscordUsers: make(map[string]bool), + MumbleUsers: make(map[string]bool), } - l := &Listener{BridgeConf, Bridge} + ul := &sync.Mutex{} + cl := &sync.Mutex{} + l := &Listener{BridgeConf, Bridge, ul, cl} // Discord setup // Open Websocket @@ -100,6 +104,10 @@ func main() { discord.AddHandler(l.guildCreate) discord.AddHandler(l.voiceUpdate) err = discord.Open() + l.BridgeConf.Config.Attach(gumbleutil.Listener{ + Connect: l.mumbleConnect, + UserChange: l.mumbleUserChange, + }) if err != nil { log.Println(err) return @@ -113,14 +121,14 @@ func main() { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) - BridgeConf.Mode = BridgeModeAuto + Bridge.Mode = BridgeModeAuto go AutoBridge(discord, l) case "manual": log.Println("bridge starting in manual mode") - BridgeConf.Mode = BridgeModeManual + Bridge.Mode = BridgeModeManual case "constant": log.Println("bridge starting in constant mode") - BridgeConf.Mode = BridgeModeConstant + Bridge.Mode = BridgeModeConstant go startBridge(discord, *discordGID, *discordCID, l, make(chan bool)) default: discord.Close() From 2e83d6d5152f112c8f8b1b476ff20c72f6cf09aa Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 8 Jan 2021 14:38:21 -0500 Subject: [PATCH 34/37] lint cleanup, remove code duplication --- bridge.go | 9 ++++--- config.go | 10 ++++--- discord.go | 4 --- handlers.go | 77 +++++++++++++++++------------------------------------ main.go | 6 ++--- mumble.go | 5 ---- 6 files changed, 39 insertions(+), 72 deletions(-) diff --git a/bridge.go b/bridge.go index 3b1b4d0..bc2cc69 100644 --- a/bridge.go +++ b/bridge.go @@ -14,10 +14,11 @@ import ( "layeh.com/gumble/gumble" ) +//BridgeState manages dynamic information about the bridge during runtime type BridgeState struct { ActiveConn chan bool Connected bool - Mode BridgeMode + Mode bridgeMode Client *gumble.Client DiscordUsers map[string]bool MumbleUsers map[string]bool @@ -157,6 +158,9 @@ func discordStatusUpdate(dg *discordgo.Session, host, port string, l *Listener) } } +//AutoBridge starts a goroutine to check the number of users in discord and mumble +//when there is at least one user on both, starts up the bridge +//when there are no users on either side, kills the bridge func AutoBridge(s *discordgo.Session, l *Listener) { log.Println("beginning auto mode") for { @@ -177,8 +181,7 @@ func AutoBridge(s *discordgo.Session, l *Listener) { if l.Bridge.Connected && l.Bridge.MumbleUserCount == 0 && l.Bridge.DiscordUserCount <= 1 { log.Println("no one online, killing bridge") l.Bridge.ActiveConn <- true - MumbleReset() - DiscordReset() + l.Bridge.ActiveConn = nil } l.UserCountLock.Unlock() } diff --git a/config.go b/config.go index cf58b82..4522085 100644 --- a/config.go +++ b/config.go @@ -10,14 +10,16 @@ import ( "layeh.com/gumble/gumble" ) -type BridgeMode int +type bridgeMode int const ( - BridgeModeAuto BridgeMode = iota - BridgeModeManual - BridgeModeConstant + bridgeModeAuto bridgeMode = iota + bridgeModeManual + bridgeModeConstant ) +//BridgeConfig holds configuration information set at startup +//It should not change during runtime type BridgeConfig struct { Config *gumble.Config MumbleAddr string diff --git a/discord.go b/discord.go index 462abb7..2f72daf 100644 --- a/discord.go +++ b/discord.go @@ -22,10 +22,6 @@ var discordMutex sync.Mutex var discordMixerMutex sync.Mutex var fromDiscordMap = make(map[uint32]fromDiscord) -func DiscordReset() { - fromDiscordMap = make(map[uint32]fromDiscord) -} - // OnError gets called by dgvoice when an error is encountered. // By default logs to STDERR var OnError = func(str string, err error) { diff --git a/handlers.go b/handlers.go index a22101f..69b0cc0 100644 --- a/handlers.go +++ b/handlers.go @@ -11,6 +11,8 @@ import ( "layeh.com/gumble/gumble" ) +//Listener holds references to the current BridgeConf +//and BridgeState for use by the event handlers type Listener struct { BridgeConf *BridgeConfig Bridge *BridgeState @@ -22,11 +24,16 @@ func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { log.Println("READY event registered") //Setup initial discord state var g *discordgo.Guild + g = nil for _, i := range event.Guilds { if i.ID == l.BridgeConf.GID { g = i } } + if g == nil { + log.Println("bad guild on READY") + return + } for _, vs := range g.VoiceStates { if vs.ChannelID == l.BridgeConf.CID { l.UserCountLock.Lock() @@ -48,7 +55,7 @@ func (l *Listener) ready(s *discordgo.Session, event *discordgo.Ready) { func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - if l.Bridge.Mode == BridgeModeConstant { + if l.Bridge.Mode == bridgeModeConstant { return } @@ -56,23 +63,21 @@ func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat if m.Author.ID == s.State.User.ID { return } + // 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 + } prefix := "!" + l.BridgeConf.Command if strings.HasPrefix(m.Content, prefix+" link") { - - // 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 guild's current voice states. for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { @@ -86,57 +91,23 @@ func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat } if strings.HasPrefix(m.Content, prefix+" unlink") { - - // 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 guild's current voice states. for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) l.Bridge.ActiveConn <- true l.Bridge.ActiveConn = nil - MumbleReset() - DiscordReset() return } } } if strings.HasPrefix(m.Content, prefix+" refresh") { - - // 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 guild's current voice states. for _, vs := range g.VoiceStates { if vs.UserID == m.Author.ID { log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) l.Bridge.ActiveConn <- true - MumbleReset() - DiscordReset() time.Sleep(5 * time.Second) l.Bridge.ActiveConn = make(chan bool) go startBridge(s, g.ID, vs.ChannelID, l, l.Bridge.ActiveConn) @@ -146,13 +117,13 @@ func (l *Listener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat } if strings.HasPrefix(m.Content, prefix+" auto") { - if l.Bridge.Mode != BridgeModeAuto { - l.Bridge.Mode = BridgeModeAuto + if l.Bridge.Mode != bridgeModeAuto { + l.Bridge.Mode = bridgeModeAuto l.Bridge.AutoChan = make(chan bool) go AutoBridge(s, l) } else { l.Bridge.AutoChan <- true - l.Bridge.Mode = BridgeModeManual + l.Bridge.Mode = bridgeModeManual } } } diff --git a/main.go b/main.go index 77ce55b..e8414d1 100644 --- a/main.go +++ b/main.go @@ -121,14 +121,14 @@ func main() { case "auto": log.Println("bridge starting in automatic mode") Bridge.AutoChan = make(chan bool) - Bridge.Mode = BridgeModeAuto + Bridge.Mode = bridgeModeAuto go AutoBridge(discord, l) case "manual": log.Println("bridge starting in manual mode") - Bridge.Mode = BridgeModeManual + Bridge.Mode = bridgeModeManual case "constant": log.Println("bridge starting in constant mode") - Bridge.Mode = BridgeModeConstant + Bridge.Mode = bridgeModeConstant go startBridge(discord, *discordGID, *discordCID, l, make(chan bool)) default: discord.Close() diff --git a/mumble.go b/mumble.go index 09141bc..24d2d4e 100644 --- a/mumble.go +++ b/mumble.go @@ -18,11 +18,6 @@ type MumbleDuplex struct { Close chan bool } -func MumbleReset() { - fromMumbleArr = []chan gumble.AudioBuffer{} - mumbleStreamingArr = []bool{} -} - // OnAudioStream - Spawn routines to handle incoming packets func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { From e035baa3e9577379cd34ea7e97f1f88ab10582a0 Mon Sep 17 00:00:00 2001 From: Steve Date: Sat, 9 Jan 2021 16:38:36 -0500 Subject: [PATCH 35/37] fix mutexes --- handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers.go b/handlers.go index 69b0cc0..64a6db2 100644 --- a/handlers.go +++ b/handlers.go @@ -201,8 +201,8 @@ func (l *Listener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceState } l.ConnectedLock.Unlock() l.Bridge.DiscordUserCount = count - l.UserCountLock.Unlock() } + l.UserCountLock.Unlock() } } From 0973c3bf25320025ec49ed96d625c9a4d9440068 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 12 Jan 2021 10:40:12 -0500 Subject: [PATCH 36/37] make NICE option toggleable --- main.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index e8414d1..c8c5611 100644 --- a/main.go +++ b/main.go @@ -29,8 +29,9 @@ func main() { discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token") discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid") discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") - discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") - mode := flag.String("mode", lookupEnvOrString("MODE", "manual"), "determine which mode the bridge starts in") + discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "DISCORD_COMMAND,Discord command string, env alt DISCORD_COMMAND, optional, defaults to mumble-discord") + mode := flag.String("mode", lookupEnvOrString("MODE", "manual"), "MODE,determine which mode the bridge starts in") + nice := flag.Bool("nice", lookupEnvOrBool("NICE", false), "NICE,whether the bridge should automatically try to 'nice' itself") flag.Parse() log.Printf("app.config %v\n", getConfig(flag.CommandLine)) @@ -53,10 +54,12 @@ func main() { if *mode == "" { log.Fatalln("missing mode set") } - //err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) - //if err != nil { - // log.Println("Unable to set priority. ", err) - //} + if *nice { + err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5) + if err != nil { + log.Println("Unable to set priority. ", err) + } + } //Connect to discord discord, err := discordgo.New("Bot " + *discordToken) From cb5ca33b0cd2cf14676cda793262d65a49b49221 Mon Sep 17 00:00:00 2001 From: Tyler Stiene Date: Sat, 16 Jan 2021 13:40:12 -0500 Subject: [PATCH 37/37] remove default case from mumble onAudioStream --- .gitignore | 4 +++- main.go | 15 +++++++++++++++ mumble.go | 1 - 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 34f636d..d25081d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .env main -mumble-discord-bridge \ No newline at end of file +mumble-discord-bridge +bridge +.prof \ No newline at end of file diff --git a/main.go b/main.go index c8c5611..2de5be3 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "log" "os" "os/signal" + "runtime/pprof" "strconv" "sync" "syscall" @@ -17,6 +18,8 @@ import ( _ "layeh.com/gumble/opus" ) +var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") + func main() { godotenv.Load() @@ -61,6 +64,18 @@ func main() { } } + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal("could not create CPU profile: ", err) + } + defer f.Close() // error handling omitted for example + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatal("could not start CPU profile: ", err) + } + defer pprof.StopCPUProfile() + } + //Connect to discord discord, err := discordgo.New("Bot " + *discordToken) if err != nil { diff --git a/mumble.go b/mumble.go index 24d2d4e..c3d143b 100644 --- a/mumble.go +++ b/mumble.go @@ -33,7 +33,6 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { log.Println("new mumble audio stream", e.User.Name) for { select { - default: case <-die: log.Println("Removing mumble audio stream") return