// OnDisconnect event. Terminates MumbleDJ process or retries connection if // automatic connection retries are enabled. func (dj *MumbleDJ) OnDisconnect(e *gumble.DisconnectEvent) { dj.Queue.Reset() if viper.GetBool("connection.retry_enabled") && (e.Type == gumble.DisconnectError || e.Type == gumble.DisconnectKicked) { logrus.WithFields(logrus.Fields{ "interval_secs": fmt.Sprintf("%d", viper.GetInt("connection.retry_interval")), "attempts": fmt.Sprintf("%d", viper.GetInt("connection.retry_attempts")), }).Warnln("Disconnected from server. Retrying connection...") success := false for retries := 0; retries < viper.GetInt("connection.retry_attempts"); retries++ { logrus.Infoln("Retrying connection...") if client, err := gumble.DialWithDialer(new(net.Dialer), viper.GetString("connection.address")+":"+viper.GetString("connection.port"), dj.GumbleConfig, dj.TLSConfig); err == nil { dj.Client = client logrus.Infoln("Successfully reconnected to the server!") success = true break } time.Sleep(time.Duration(viper.GetInt("connection.retry_interval")) * time.Second) } if !success { dj.KeepAlive <- true logrus.Fatalln("Could not reconnect to server. Exiting...") } } else { dj.KeepAlive <- true logrus.Fatalln("Disconnected from server. No reconnect attempts will be made.") } }
// Main aids in the creation of a basic command line gumble bot. It accepts the // following flag arguments: // --server // --username // --password // --insecure, // --certificate // --key func Main(listeners ...gumble.EventListener) { server := flag.String("server", "localhost:64738", "Mumble server address") username := flag.String("username", "gumble-bot", "client username") password := flag.String("password", "", "client password") insecure := flag.Bool("insecure", false, "skip server certificate verification") certificateFile := flag.String("certificate", "", "user certificate file (PEM)") keyFile := flag.String("key", "", "user certificate key file (PEM)") if !flag.Parsed() { flag.Parse() } host, port, err := net.SplitHostPort(*server) if err != nil { host = *server port = strconv.Itoa(gumble.DefaultPort) } keepAlive := make(chan bool) config := gumble.NewConfig() config.Username = *username config.Password = *password address := net.JoinHostPort(host, port) var tlsConfig tls.Config if *insecure { tlsConfig.InsecureSkipVerify = true } if *certificateFile != "" { if *keyFile == "" { keyFile = certificateFile } if certificate, err := tls.LoadX509KeyPair(*certificateFile, *keyFile); err != nil { fmt.Printf("%s: %s\n", os.Args[0], err) os.Exit(1) } else { tlsConfig.Certificates = append(tlsConfig.Certificates, certificate) } } config.Attach(AutoBitrate) for _, listener := range listeners { config.Attach(listener) } config.Attach(Listener{ Disconnect: func(e *gumble.DisconnectEvent) { keepAlive <- true }, }) _, err = gumble.DialWithDialer(new(net.Dialer), address, config, &tlsConfig) if err != nil { fmt.Printf("%s: %s\n", os.Args[0], err) os.Exit(1) } <-keepAlive }
// Connect starts the process for connecting to a Mumble server. func (dj *MumbleDJ) Connect() error { // Perform startup checks before connecting. logrus.Infoln("Performing startup checks...") PerformStartupChecks() // Create Gumble config. dj.GumbleConfig = gumble.NewConfig() dj.GumbleConfig.Username = viper.GetString("connection.username") dj.GumbleConfig.Password = viper.GetString("connection.password") dj.GumbleConfig.Tokens = strings.Split(viper.GetString("connection.access_tokens"), ",") // Initialize key pair if needed. if viper.GetBool("connection.insecure") { dj.TLSConfig.InsecureSkipVerify = true } else { dj.TLSConfig.ServerName = viper.GetString("connection.address") if viper.GetString("connection.cert") != "" { if viper.GetString("connection.key") == "" { viper.Set("connection.key", viper.GetString("connection.cert")) } if certificate, err := tls.LoadX509KeyPair(viper.GetString("connection.cert"), viper.GetString("connection.key")); err == nil { dj.TLSConfig.Certificates = append(dj.TLSConfig.Certificates, certificate) } else { return err } } } // Add user p12 cert if needed. if viper.GetString("connection.user_p12") != "" { if _, err := os.Stat(viper.GetString("connection.user_p12")); os.IsNotExist(err) { return err } // Create temporary directory for converted p12 file. dir, err := ioutil.TempDir("", "mumbledj") if err != nil { return err } defer os.RemoveAll(dir) // Create temporary mumbledj.crt.pem from p12 file. command := exec.Command("openssl", "pkcs12", "-password", "pass:"******"-in", viper.GetString("connection.user_p12"), "-out", dir+"/mumbledj.crt.pem", "-clcerts", "-nokeys") if err := command.Run(); err != nil { return err } // Create temporary mumbledj.key.pem from p12 file. command = exec.Command("openssl", "pkcs12", "-password", "pass:"******"-in", viper.GetString("connection.user_p12"), "-out", dir+"/mumbledj.key.pem", "-nocerts", "-nodes") if err := command.Run(); err != nil { return err } if certificate, err := tls.LoadX509KeyPair(dir+"/mumbledj.crt.pem", dir+"/mumbledj.key.pem"); err == nil { dj.TLSConfig.Certificates = append(dj.TLSConfig.Certificates, certificate) } else { return err } } dj.GumbleConfig.Attach(gumbleutil.Listener{ Connect: dj.OnConnect, Disconnect: dj.OnDisconnect, TextMessage: dj.OnTextMessage, UserChange: dj.OnUserChange, }) dj.GumbleConfig.Attach(gumbleutil.AutoBitrate) var connErr error logrus.WithFields(logrus.Fields{ "address": viper.GetString("connection.address"), "port": viper.GetString("connection.port"), }).Infoln("Attempting connection to server...") if dj.Client, connErr = gumble.DialWithDialer(new(net.Dialer), viper.GetString("connection.address")+":"+viper.GetString("connection.port"), dj.GumbleConfig, dj.TLSConfig); connErr != nil { return connErr } return nil }