1
0
mirror of https://github.com/stryan/mumble-discord-bridge.git synced 2024-12-30 09:55:40 -05:00

initial changes, make it manual linking, show mumble users in status

This commit is contained in:
stryan 2020-12-29 15:14:19 -05:00
parent d9d30929ff
commit 5a96c488ee
4 changed files with 285 additions and 103 deletions

57
config.go Normal file
View File

@ -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
}

91
handlers.go Normal file
View File

@ -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
}
}
}

121
main.go
View File

@ -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")
}

119
yammer.go Normal file
View File

@ -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")
}