package main import ( "flag" "fmt" "log" "os" "os/signal" "strconv" "time" "github.com/bwmarrin/discordgo" "github.com/joho/godotenv" "layeh.com/gumble/gumble" _ "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 } func main() { 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") // mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), "mumble insecure, env alt MUMBLE_INSECURE") 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") } // Shared Channels // Shared channels pass PCM information in 10ms chunks [480]int16 var toMumble = make(chan gumble.AudioBuffer, 100) var toDiscord = make(chan []int16, 100) // MUMBLE config := gumble.NewConfig() config.Username = *mumbleUsername config.Password = *mumblePassword config.AudioInterval = time.Millisecond * 10 m := MumbleDuplex{} mumble, err := gumble.Dial(*mumbleAddr+":"+strconv.Itoa(*mumblePort), config) if err != nil { log.Println(err) return } go m.fromMumbleMixer(toDiscord) go m.sendToMumble(mumble, toMumble) config.AudioListeners.Attach(m) log.Println("Mumble Connected") // DISCORD discord, err := discordgo.New("Bot " + *discordToken) if err != nil { log.Println(err) return } // Open Websocket err = discord.Open() if err != nil { log.Println(err) return } log.Println("Discord Bot Connected") dgv, err := discord.ChannelVoiceJoin(*discordGID, *discordCID, false, false) if err != nil { log.Println(err) return } go discordReceivePCM(dgv, toMumble) go discordSendPCM(dgv, toDiscord) // Wait for Exit Signal c := make(chan os.Signal) signal.Notify(c, os.Interrupt) select { case sig := <-c: log.Printf("\nGot %s signal. Ending Mumble-Bridge\n", sig) mumble.Disconnect() dgv.Speaking(false) dgv.Close() discord.Close() } }