1
0
mirror of https://github.com/stryan/mumble-discord-bridge.git synced 2024-11-23 05:45:41 -05:00

use user counting, add discord buffer debuf, intial auto mode

This commit is contained in:
stryan 2021-01-03 15:32:59 -05:00
parent ca1ba1d099
commit 4b8e6eea55
5 changed files with 133 additions and 47 deletions

View File

@ -22,7 +22,7 @@ func startBridge(discord *discordgo.Session, discordGID string, discordCID strin
} }
defer dgv.Speaking(false) defer dgv.Speaking(false)
defer dgv.Close() defer dgv.Close()
Bridge.Connected = true
discord.ShouldReconnectOnError = true discord.ShouldReconnectOnError = true
// MUMBLE Setup // 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) log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig)
case <-die: case <-die:
log.Println("\nGot internal die request. Terminating Mumble-Bridge") log.Println("\nGot internal die request. Terminating Mumble-Bridge")
close(toMumble)
dgv.Disconnect() dgv.Disconnect()
log.Println("Closing mumble threads")
det.Detach() det.Detach()
Bridge.Connected = false
} }
} }
func pingMumble(host, port string, c chan int) { func pingMumble(host, port string, c chan int) {
m, _ := time.ParseDuration("30s") m, _ := time.ParseDuration("30s")
curr := 0 curr := 0
log.Println("Started mumble ping loop")
for { for {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
resp, err := gumble.Ping(host+":"+port, -1, m) resp, err := gumble.Ping(host+":"+port, -1, m)
curr = resp.ConnectedUsers
if err != nil { if err != nil {
panic(err) panic(err)
} }
if resp.ConnectedUsers-1 != curr { if Bridge.Connected {
curr = resp.ConnectedUsers - 1 curr = curr - 1
log.Printf("Now %v users in mumble\n", curr) }
if curr > 0 { if curr != Bridge.MumbleUserCount {
c <- curr Bridge.MumbleUserCount = curr
} c <- Bridge.MumbleUserCount
} }
} }
log.Println("Mumble ping loop broken")
} }
func discordStatusUpdate(dg *discordgo.Session, c chan int) { func discordStatusUpdate(dg *discordgo.Session, c chan int) {
status := "" status := ""
curr := 0 curr := 0
log.Println("Started discord control loop")
for { for {
curr = <-c curr = <-c
log.Println("Updating discord status")
if curr == 0 { if curr == 0 {
status = "" status = ""
} else { } else {
@ -121,5 +117,28 @@ func discordStatusUpdate(dg *discordgo.Session, c chan int) {
} }
dg.UpdateListeningStatus(status) 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
}
}
} }

View File

@ -10,12 +10,22 @@ import (
"layeh.com/gumble/gumble" "layeh.com/gumble/gumble"
) )
type YammerConfig struct { type BridgeConfig struct {
Config *gumble.Config Config *gumble.Config
MumbleAddr string MumbleAddr string
MumbleInsecure bool MumbleInsecure bool
Die chan bool Auto bool
ActiveConns map[string]chan 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 { func lookupEnvOrString(key string, defaultVal string) string {

View File

@ -98,7 +98,6 @@ func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16, die chan b
lastReady = true lastReady = true
readyTimeout.Stop() readyTimeout.Stop()
} }
log.Println("sending packets to mumble")
v.OpusSend <- opus v.OpusSend <- opus
} else { } else {
if streaming { if streaming {
@ -118,14 +117,12 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) {
var readyTimeout *time.Timer var readyTimeout *time.Timer
for { for {
log.Println("Start loop")
select { select {
case <-die: case <-die:
log.Println("killing discord ReceivePCM") log.Println("killing discord ReceivePCM")
return return
default: default:
} }
log.Println("checked for death")
if v.Ready == false || v.OpusRecv == nil { if v.Ready == false || v.OpusRecv == nil {
if lastReady == true { if lastReady == true {
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) 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() readyTimeout.Stop()
} }
log.Println("rec1")
p, ok := <-v.OpusRecv p, ok := <-v.OpusRecv
log.Println("rec2")
if !ok { if !ok {
log.Println("Opus not ok") log.Println("Opus not ok")
continue continue
} }
discordMutex.Lock() discordMutex.Lock()
log.Println("got lock one")
_, ok = fromDiscordMap[p.SSRC] _, ok = fromDiscordMap[p.SSRC]
discordMutex.Unlock() discordMutex.Unlock()
if !ok { if !ok {
@ -169,20 +163,28 @@ func discordReceivePCM(v *discordgo.VoiceConnection, die chan bool) {
} }
discordMutex.Lock() discordMutex.Lock()
log.Println("got lock 2")
p.PCM, err = fromDiscordMap[p.SSRC].decoder.Decode(p.Opus, 960, false) p.PCM, err = fromDiscordMap[p.SSRC].decoder.Decode(p.Opus, 960, false)
discordMutex.Unlock() discordMutex.Unlock()
if err != nil { if err != nil {
OnError("Error decoding opus data", err) OnError("Error decoding opus data", err)
continue continue
} }
if len(p.PCM) != 960 {
log.Println("Opus size error")
continue
}
discordMutex.Lock() discordMutex.Lock()
log.Println("got lock 3") select {
fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480] case fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480]:
fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960] 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() discordMutex.Unlock()
log.Println("finish loop")
} }
} }
@ -237,7 +239,6 @@ func fromDiscordMixer(toMumble chan<- gumble.AudioBuffer, die chan bool) {
if sendAudio { if sendAudio {
select { select {
case toMumble <- outBuf: case toMumble <- outBuf:
log.Println("sending to mumble")
default: default:
log.Println("toMumble buffer full. Dropping packet") log.Println("toMumble buffer full. Dropping packet")
} }

View File

@ -20,8 +20,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.State.User.ID { if m.Author.ID == s.State.User.ID {
return return
} }
prefix := "!" + BridgeConf.Command
if strings.HasPrefix(m.Content, "!yammer link") { if strings.HasPrefix(m.Content, prefix+" link") {
// Find the channel that the message came from. // Find the channel that the message came from.
c, err := s.State.Channel(m.ChannelID) 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 { if vs.UserID == m.Author.ID {
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
die := make(chan bool) die := make(chan bool)
YBConfig.ActiveConns[vs.ChannelID] = die Bridge.ActiveConn = die
go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, die) go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, die)
return return
} }
} }
} }
if strings.HasPrefix(m.Content, "!yammer unlink") { if strings.HasPrefix(m.Content, prefix+" unlink") {
// Find the channel that the message came from. // Find the channel that the message came from.
c, err := s.State.Channel(m.ChannelID) c, err := s.State.Channel(m.ChannelID)
@ -69,8 +69,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if vs.UserID == m.Author.ID { if vs.UserID == m.Author.ID {
log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID)
YBConfig.ActiveConns[vs.ChannelID] <- true Bridge.ActiveConn <- true
YBConfig.ActiveConns[vs.ChannelID] = nil Bridge.ActiveConn = nil
MumbleReset() MumbleReset()
DiscordReset() DiscordReset()
return 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. // Find the channel that the message came from.
c, err := s.State.Channel(m.ChannelID) c, err := s.State.Channel(m.ChannelID)
@ -98,16 +98,27 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
for _, vs := range g.VoiceStates { for _, vs := range g.VoiceStates {
if vs.UserID == m.Author.ID { if vs.UserID == m.Author.ID {
log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID) log.Printf("Trying to refresh GID %v and VID %v\n", g.ID, vs.ChannelID)
YBConfig.ActiveConns[vs.ChannelID] <- true Bridge.ActiveConn <- true
MumbleReset() MumbleReset()
DiscordReset() DiscordReset()
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
YBConfig.ActiveConns[vs.ChannelID] = make(chan bool) Bridge.ActiveConn = make(chan bool)
go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, YBConfig.ActiveConns[vs.ChannelID]) go startBridge(s, g.ID, vs.ChannelID, BridgeConf.Config, BridgeConf.MumbleAddr, BridgeConf.MumbleInsecure, Bridge.ActiveConn)
return 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) { 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
}

25
main.go
View File

@ -15,7 +15,8 @@ import (
_ "layeh.com/gumble/opus" _ "layeh.com/gumble/opus"
) )
var YBConfig *YammerConfig var BridgeConf *BridgeConfig
var Bridge *BridgeState
func main() { func main() {
godotenv.Load() godotenv.Load()
@ -29,7 +30,7 @@ func main() {
discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token") 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") discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid")
discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid") 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() flag.Parse()
log.Printf("app.config %v\n", getConfig(flag.CommandLine)) log.Printf("app.config %v\n", getConfig(flag.CommandLine))
@ -66,6 +67,7 @@ func main() {
discord.AddHandler(ready) discord.AddHandler(ready)
discord.AddHandler(messageCreate) discord.AddHandler(messageCreate)
discord.AddHandler(guildCreate) discord.AddHandler(guildCreate)
discord.AddHandler(voiceUpdate)
err = discord.Open() err = discord.Open()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -74,19 +76,30 @@ func main() {
defer discord.Close() defer discord.Close()
log.Println("Discord Bot Connected") log.Println("Discord Bot Connected")
log.Printf("Discord bot looking for command !%v", *discordCommand)
config := gumble.NewConfig() config := gumble.NewConfig()
config.Username = *mumbleUsername config.Username = *mumbleUsername
config.Password = *mumblePassword config.Password = *mumblePassword
config.AudioInterval = time.Millisecond * 10 config.AudioInterval = time.Millisecond * 10
YBConfig = &YammerConfig{ BridgeConf = &BridgeConfig{
Config: config, Config: config,
MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort), MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort),
MumbleInsecure: *mumbleInsecure, MumbleInsecure: *mumbleInsecure,
ActiveConns: make(map[string]chan bool), Auto: false,
Command: *discordCommand,
GID: *discordGID,
CID: *discordCID,
} }
Bridge = &BridgeState{
//go startBridge(discord, *discordGID, *discordCID, config, *mumbleAddr+":"+strconv.Itoa(*mumblePort), *mumbleInsecure, die) 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) sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)