2020-10-29 02:21:07 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-11-28 03:46:21 -05:00
|
|
|
"crypto/tls"
|
2020-10-29 02:21:07 -04:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2020-11-28 03:46:21 -05:00
|
|
|
"net"
|
2020-10-29 02:21:07 -04:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"strconv"
|
2021-01-04 21:23:52 -05:00
|
|
|
"syscall"
|
2020-10-29 02:21:07 -04:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"layeh.com/gumble/gumble"
|
|
|
|
_ "layeh.com/gumble/opus"
|
|
|
|
)
|
|
|
|
|
2021-01-09 18:18:31 -05:00
|
|
|
var (
|
|
|
|
// Build vars
|
|
|
|
version string
|
|
|
|
commit string
|
|
|
|
date string
|
|
|
|
)
|
|
|
|
|
2020-10-29 02:21:07 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2021-01-09 18:18:31 -05:00
|
|
|
log.Println("Mumble-Discord-Bridge")
|
|
|
|
log.Println("v" + version + " " + commit + " " + date)
|
|
|
|
|
2020-10-29 02:21:07 -04:00
|
|
|
godotenv.Load()
|
|
|
|
|
|
|
|
mumbleAddr := flag.String("mumble-address", lookupEnvOrString("MUMBLE_ADDRESS", ""), "MUMBLE_ADDRESS, mumble server address, example example.com")
|
|
|
|
mumblePort := flag.Int("mumble-port", lookupEnvOrInt("MUMBLE_PORT", 64738), "MUMBLE_PORT mumble port")
|
|
|
|
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")
|
2020-11-28 03:46:21 -05:00
|
|
|
mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), "mumble insecure, env alt MUMBLE_INSECURE")
|
2020-10-29 02:21:07 -04:00
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
log.Printf("app.config %v\n", getConfig(flag.CommandLine))
|
|
|
|
|
|
|
|
if *mumbleAddr == "" {
|
|
|
|
log.Fatalln("missing mumble address")
|
|
|
|
}
|
|
|
|
if *mumbleUsername == "" {
|
|
|
|
log.Fatalln("missing mumble username")
|
|
|
|
}
|
|
|
|
|
|
|
|
if *discordToken == "" {
|
|
|
|
log.Fatalln("missing discord bot token")
|
|
|
|
}
|
|
|
|
if *discordGID == "" {
|
|
|
|
log.Fatalln("missing discord gid")
|
|
|
|
}
|
|
|
|
if *discordCID == "" {
|
|
|
|
log.Fatalln("missing discord cid")
|
|
|
|
}
|
|
|
|
|
2021-01-09 18:18:31 -05:00
|
|
|
// Attempt to set Process Priority
|
|
|
|
// This is allowed to fail
|
|
|
|
|
2021-01-04 21:23:52 -05:00
|
|
|
err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5)
|
|
|
|
if err != nil {
|
2021-01-09 18:18:31 -05:00
|
|
|
log.Println("Unable to set process priority. ", err)
|
2021-01-04 21:23:52 -05:00
|
|
|
}
|
|
|
|
|
2020-10-30 01:38:01 -04:00
|
|
|
// DISCORD Setup
|
|
|
|
|
2020-10-29 02:21:07 -04:00
|
|
|
discord, err := discordgo.New("Bot " + *discordToken)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open Websocket
|
2021-01-09 18:18:31 -05:00
|
|
|
discord.LogLevel = 1
|
2020-10-29 02:21:07 -04:00
|
|
|
err = discord.Open()
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2020-11-04 01:12:43 -05:00
|
|
|
defer discord.Close()
|
2020-10-29 02:21:07 -04:00
|
|
|
|
|
|
|
log.Println("Discord Bot Connected")
|
|
|
|
|
|
|
|
dgv, err := discord.ChannelVoiceJoin(*discordGID, *discordCID, false, false)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2020-11-04 01:12:43 -05:00
|
|
|
defer dgv.Speaking(false)
|
|
|
|
defer dgv.Close()
|
|
|
|
|
|
|
|
discord.ShouldReconnectOnError = true
|
2020-10-29 02:21:07 -04:00
|
|
|
|
2020-11-04 01:12:43 -05:00
|
|
|
// MUMBLE Setup
|
|
|
|
|
|
|
|
config := gumble.NewConfig()
|
|
|
|
config.Username = *mumbleUsername
|
|
|
|
config.Password = *mumblePassword
|
|
|
|
config.AudioInterval = time.Millisecond * 10
|
|
|
|
|
|
|
|
m := MumbleDuplex{}
|
2020-11-30 19:23:55 -05:00
|
|
|
ml := MumbleEventListener{}
|
2020-11-04 01:12:43 -05:00
|
|
|
|
2020-11-28 03:46:21 -05:00
|
|
|
var tlsConfig tls.Config
|
|
|
|
if *mumbleInsecure {
|
|
|
|
tlsConfig.InsecureSkipVerify = true
|
|
|
|
}
|
|
|
|
|
2021-01-04 21:23:52 -05:00
|
|
|
mumble, err := gumble.DialWithDialer(new(net.Dialer), *mumbleAddr+":"+strconv.Itoa(*mumblePort), config, &tlsConfig)
|
2020-11-28 03:46:21 -05:00
|
|
|
|
2020-11-04 01:12:43 -05:00
|
|
|
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)
|
2020-11-30 19:23:55 -05:00
|
|
|
config.Listeners.Attach(ml)
|
2020-11-04 01:12:43 -05:00
|
|
|
//Discord
|
2020-11-11 22:57:51 -05:00
|
|
|
go discordReceivePCM(dgv, die)
|
2020-10-30 01:38:01 -04:00
|
|
|
go fromDiscordMixer(toMumble)
|
2020-11-11 22:57:51 -05:00
|
|
|
go discordSendPCM(dgv, toDiscord, die)
|
2020-10-29 02:21:07 -04:00
|
|
|
|
|
|
|
// Wait for Exit Signal
|
|
|
|
c := make(chan os.Signal)
|
|
|
|
signal.Notify(c, os.Interrupt)
|
|
|
|
|
2020-11-04 01:12:43 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-10-29 02:21:07 -04:00
|
|
|
select {
|
|
|
|
case sig := <-c:
|
2020-11-04 01:12:43 -05:00
|
|
|
log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig)
|
|
|
|
case <-die:
|
|
|
|
log.Println("\nGot internal die request. Terminating Mumble-Bridge")
|
2020-10-29 02:21:07 -04:00
|
|
|
}
|
2020-11-30 19:23:55 -05:00
|
|
|
time.AfterFunc(5*time.Second, func() {
|
|
|
|
log.Println("Graceful disconnect timeout, Forcing!")
|
|
|
|
os.Exit(9)
|
|
|
|
})
|
2020-10-29 02:21:07 -04:00
|
|
|
}
|