// 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 }
func main() { address := os.Getenv("FUMBLE_ADDRESS") username := os.Getenv("FUMBLE_USERNAME") password := os.Getenv("FUMBLE_PASSWORD") // insecure ins := os.Getenv("FUMBLE_INSECURE") insecure := (ins == "true") // end: insecure // certificate certificateFile := os.Getenv("FUMBLE_CERTIFICATE") keyFile := os.Getenv("FUMBLE_KEY") // end: certificate // rpc address rpc_port := os.Getenv("FUMBLE_RPC_PORT") if rpc_port == "" { rpc_port = "7070" } // end: rpc address config := gumble.NewConfig() config.Address = address config.Username = username config.Password = password if insecure { config.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 { config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate) } } m := mumble.M m.Config = config m.Create() err = m.Connect() if err != nil { log.Fatal(err) } // rpc server server.Start(m, rpc_port) <-m.KeepAlive }
func main() { // setting configuration defaultchannel := "DragonHacks2016" config := gumble.NewConfig() config.Username = "******" config.Address = "rlyshw.com:64738" config.TLSConfig.InsecureSkipVerify = true keepAlive := make(chan bool) // generatiing a client instance client := gumble.NewClient(config) client.Attach(gumbleutil.AutoBitrate) // attaching client client.Attach(gumbleutil.Listener{ TextMessage: func(e *gumble.TextMessageEvent) { fmt.Printf("Message received: %s\n", e.Message) e.Message = "joining" client.Send(e) }, }) // on-connect, perform the following client.Attach(gumbleutil.Listener{ Connect: func(e *gumble.ConnectEvent) { fmt.Printf("Connected!!: %s\n", e.MaximumBitrate) fmt.Printf("User Channel: %s\n", client.Self.Channel.Name) for i, _ := range client.Channels { if client.Channels[i].Name == defaultchannel { client.Self.Move(client.Channels[i]) break } } // outlining for audio operations }, }) client.Attach(gumbleutil.Listener{ Disconnect: func(e *gumble.DisconnectEvent) { keepAlive <- true }, }) // connecting the client if err := client.Connect(); err != nil { panic(err) os.Exit(1) } // concurrency??? <-keepAlive }
func (k *Mumble) Start(fl map[string]string, s *services.Services) { k.services = s //Config k.Config = gumble.NewConfig() k.Config.Username = fl["Flag_mumble_username"] k.Config.Password = fl["Flag_mumble_password"] k.Config.Address = fl["Flag_mumble_server"] k.Config.TLSConfig.InsecureSkipVerify = true k.conf_volume = 1.0 //Client creation k.Client = gumble.NewClient(k.Config) //Attach listeners k.Client.Attach(gumbleutil.AutoBitrate) k.Client.Attach(k) //TLS if fl["Flag_mumble_cert_lock"] != "" { gumbleutil.CertificateLockFile(k.Client, fl["Flag_mumble_cert_lock"]) } if _, err := os.Stat("./config/Kotik.pem"); err == nil && fl["Flag_dev"] == "false" { fl["Flag_mumble_cert"] = "./config/Kotik.pem" } else if _, err := os.Stat("./config/Kotik-dev.pem"); err == nil && fl["Flag_dev"] == "true" { fl["Flag_mumble_cert"] = "./config/Kotik-dev.pem" } if fl["Flag_mumble_cert"] != "" { if fl["Flag_mumble_cert_key"] == "" { fl["Flag_mumble_cert_key"] = fl["Flag_mumble_cert"] } if certificate, err := tls.LoadX509KeyPair(fl["Flag_mumble_cert"], fl["Flag_mumble_cert_key"]); err != nil { panic(err) } else { k.Config.TLSConfig.Certificates = append(k.Config.TLSConfig.Certificates, certificate) } } //Connect flag := false for flag == false { if err := k.Client.Connect(); err != nil { time.Sleep(time.Second * 5) } else { flag = true } } }
func main() { // Command line flags server := flag.String("server", "localhost:64738", "the server to connect to") username := flag.String("username", "", "the username of the client") insecure := flag.Bool("insecure", false, "skip server certificate verification") certificate := flag.String("certificate", "", "PEM encoded certificate and private key") flag.Parse() // Initialize b := barnard.Barnard{} b.Ui = uiterm.New(&b) // Gumble b.Config = gumble.NewConfig() b.Config.Username = *username b.Config.Address = *server if *insecure { b.Config.TLSConfig.InsecureSkipVerify = true } if *certificate != "" { if cert, err := tls.LoadX509KeyPair(*certificate, *certificate); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } else { b.Config.TLSConfig.Certificates = []tls.Certificate{cert} } } b.Client = gumble.NewClient(b.Config) b.Client.Attach(gumbleutil.AutoBitrate) b.Client.Attach(&b) // Audio if os.Getenv("ALSOFT_LOGLEVEL") == "" { os.Setenv("ALSOFT_LOGLEVEL", "0") } if stream, err := gumble_openal.New(b.Client); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } else { b.Stream = stream } if err := b.Client.Connect(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } b.Ui.Run() }
// Main aids in the creation of a basic command line gumble bot. It accepts the // following flag arguments: --server, --username, --password, --insecure, // --certificate, and --key. // // If init is non-nil, it is called before attempting to connect to the server. func Main(init func(client *gumble.Client), listener 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() } keepAlive := make(chan bool) // client config := gumble.NewConfig() config.Username = *username config.Password = *password config.Address = *server client := gumble.NewClient(config) if *insecure { config.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 { config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate) } } client.Attach(listener) client.Attach(Listener{ Disconnect: func(e *gumble.DisconnectEvent) { keepAlive <- true }, }) if init != nil { init(client) } if err := client.Connect(); err != nil { fmt.Printf("%s: %s\n", os.Args[0], err) os.Exit(1) } <-keepAlive }
func main() { var config struct { MumbleAddress string `envconfig:"MUMBLE_ADDR" default:"127.0.0.1:64738"` MumbleUsername string `envconfig:"MUMBLE_USERNAME" default:"SuperUser"` MumblePassword string `envconfig:"MUMBLE_PASSWORD" required:"true"` DBAddr string `envconfig:"DATABASE_ADDR" default:"127.0.0.1:5432"` DBName string `envconfig:"DATABASE_NAME" default:"tf2stadium"` DBUsername string `envconfig:"DATABASE_USERNAME" default:"tf2stadium"` DBPassword string `envconfig:"DATABASE_PASSWORD" default:"dickbutt"` RabbitMQURL string `envconfig:"RABBITMQ_URL" default:"amqp://*****:*****@localhost:5672/"` RabbitMQEventQueue string `envconfig:"RABBITMQ_EVENT_QUEUE" default:"events"` RabbitMQRPCQueue string `envconfig:"RABBITMQ_RPC_QUEUE" default:"fumble"` ProfilerAddr string `envconfig:"PROFILER_ADDR"` } log.SetFlags(log.Lshortfile) err := envconfig.Process("FUMBLE", &config) if err != nil { log.Fatal(err) } database.Connect(config.DBAddr, config.DBName, config.DBUsername, config.DBPassword) mumbleConf := gumble.NewConfig() mumbleConf.Username = config.MumbleUsername mumbleConf.Password = config.MumblePassword if config.ProfilerAddr != "" { go func() { log.Println(http.ListenAndServe(config.ProfilerAddr, nil)) }() log.Printf("Running profiler at %s", config.ProfilerAddr) } mumble.Connect(mumbleConf, config.MumbleAddress, config.RabbitMQURL, config.RabbitMQEventQueue) rpc.StartRPC(config.RabbitMQURL, config.RabbitMQRPCQueue) }
func main() { // Flags username := flag.String("username", "piepan-bot", "username of the bot") password := flag.String("password", "", "user password") server := flag.String("server", "localhost:64738", "address of the server") certificateFile := flag.String("certificate", "", "user certificate file (PEM)") keyFile := flag.String("key", "", "user certificate key file (PEM)") insecure := flag.Bool("insecure", false, "skip certificate checking") lock := flag.String("lock", "", "server certificate lock file") ffmpeg := flag.String("ffmpeg", "ffmpeg", "ffmpeg-capable executable for media streaming") var accessTokens strFlagSlice flag.Var(&accessTokens, "access-token", "server access token (can be defined multiple times)") flag.Usage = func() { fmt.Fprintf(os.Stderr, "piepan v0.7.0\n") fmt.Fprintf(os.Stderr, "usage: %s [options] [script files]\n", os.Args[0]) fmt.Fprintf(os.Stderr, "an easy to use framework for writing scriptable Mumble bots\n") flag.PrintDefaults() fmt.Fprintf(os.Stderr, "\nScript files are defined in the following way:\n") fmt.Fprintf(os.Stderr, " [type%c[environment%c]]filename\n", os.PathListSeparator, os.PathListSeparator) fmt.Fprintf(os.Stderr, " filename: path to script file\n") fmt.Fprintf(os.Stderr, " type: type of script file (default: file extension)\n") fmt.Fprintf(os.Stderr, " environment: name of environment where script will be executed (default: type)\n\n") fmt.Fprintf(os.Stderr, "Enabled script types:\n") fmt.Fprintf(os.Stderr, " Type Name\n") for _, ext := range piepan.PluginExtensions { fmt.Fprintf(os.Stderr, " %-12s %s\n", ext, piepan.Plugins[ext].Name) } } flag.Parse() // Configuration config := gumble.NewConfig() config.Username = *username config.Password = *password config.Address = *server config.Tokens = gumble.AccessTokens(accessTokens) client := gumble.NewClient(config) instance := piepan.New(client) audio := gumble_ffmpeg.New(client) audio.Command = *ffmpeg instance.Audio = audio if *insecure { config.TLSConfig.InsecureSkipVerify = true } if *lock != "" { gumbleutil.CertificateLockFile(client, *lock) } if *certificateFile != "" { if *keyFile == "" { keyFile = certificateFile } if certificate, err := tls.LoadX509KeyPair(*certificateFile, *keyFile); err != nil { panic(err) } else { config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate) } } client.Attach(gumbleutil.AutoBitrate) // Load scripts for _, script := range flag.Args() { if err := instance.LoadScript(script); err != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", script, err) } } keepAlive := make(chan bool) exitStatus := 0 client.Attach(gumbleutil.Listener{ Disconnect: func(e *gumble.DisconnectEvent) { if e.Type != gumble.DisconnectUser { exitStatus = int(e.Type) + 1 } keepAlive <- true }, }) if err := client.Connect(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } <-keepAlive os.Exit(exitStatus) }
func (a *adapter) startMumbleConnection() error { config := gumble.NewConfig() config.Address = net.JoinHostPort(a.server, strconv.Itoa(a.port)) config.Username = a.Robot.Name config.Password = a.password config.TLSConfig.InsecureSkipVerify = true config.AudioInterval = 10 * time.Millisecond conn := gumble.NewClient(config) conn.Attach(gumbleutil.AutoBitrate) conn.Attach(&gumbleutil.Listener{ Connect: func(e *gumble.ConnectEvent) { hal.Logger.Debug("mumble: connected") for _, channel := range e.Client.Channels { if strings.ToLower(channel.Name) == a.channel { e.Client.Self.Move(channel) break } } a.setSelfAvatar() go a.ttsGoRoutine() }, Disconnect: func(e *gumble.DisconnectEvent) { close(a.speakChan) }, TextMessage: func(e *gumble.TextMessageEvent) { message := a.newTextMessage(e) if message == nil { return } if err := a.Receive(message); err != nil { hal.Logger.Errorf("mumble: error in message receive: %v", err) } }, UserChange: func(e *gumble.UserChangeEvent) { // TODO: Detect leaving if e.Type&gumble.UserChangeChannel != gumble.UserChangeChannel { // We only care about joining and leaving return } if e.User.Session == e.Client.Self.Session { return // Ignore our own events } if e.User.Channel.ID != e.Client.Self.Channel.ID { return // If they aren't joining this channel, ignore them } msg := &hal.Message{ Type: hal.ENTER, User: hal.User{ ID: e.User.Name, Name: e.User.Name, }, Room: e.User.Channel.Name, } if err := a.Receive(msg); err != nil { hal.Logger.Errorf("mumble: error in enter receive: %v", err) } }, }) a.client = conn err := conn.Connect() if err != nil { return err } hal.Logger.Debug("mumble: connecting...") return nil }
func main() { // Flags username := flag.String("username", "piepan-bot", "username of the bot") password := flag.String("password", "", "user password") server := flag.String("server", "localhost:64738", "address of the server") certificateFile := flag.String("certificate", "", "user certificate file (PEM)") keyFile := flag.String("key", "", "user certificate key file (PEM)") insecure := flag.Bool("insecure", false, "skip certificate checking") lock := flag.String("lock", "", "server certificate lock file") ffmpeg := flag.String("ffmpeg", "ffmpeg", "ffmpeg-capable executable for media streaming") var accessTokens strFlagSlice flag.Var(&accessTokens, "access-token", "server access token (can be defined multiple times)") flag.Usage = func() { fmt.Fprintf(os.Stderr, "piepan v0.9.0\n") fmt.Fprintf(os.Stderr, "usage: %s [options] [script files]\n", os.Args[0]) fmt.Fprintf(os.Stderr, "an easy to use framework for writing Mumble bots using Lua\n") flag.PrintDefaults() } flag.Parse() // Configuration config := gumble.NewConfig() config.Username = *username config.Password = *password config.Address = *server config.Tokens = gumble.AccessTokens(accessTokens) client := gumble.NewClient(config) instance := piepan.New(client) instance.AudioCommand = *ffmpeg if *insecure { config.TLSConfig.InsecureSkipVerify = true } if *lock != "" { gumbleutil.CertificateLockFile(client, *lock) } if *certificateFile != "" { if *keyFile == "" { keyFile = certificateFile } certificate, err := tls.LoadX509KeyPair(*certificateFile, *keyFile) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate) } client.Attach(gumbleutil.AutoBitrate) // Load scripts for _, script := range flag.Args() { if err := instance.LoadFile(script); err != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", script, err) } } keepAlive := make(chan bool) exitStatus := 0 client.Attach(gumbleutil.Listener{ Disconnect: func(e *gumble.DisconnectEvent) { if e.Type != gumble.DisconnectUser { exitStatus = int(e.Type) + 1 } keepAlive <- true }, }) if err := client.Connect(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } <-keepAlive os.Exit(exitStatus) }
func main() { godotenv.Load() bot, err := tgbotapi.NewBotAPI(os.Getenv("TG_TOKEN")) if err != nil { log.Fatal("Error creating bot: " + err.Error()) } u := tgbotapi.NewUpdate(0) u.Timeout = 60 updates, err := bot.GetUpdatesChan(u) if err != nil { log.Panic(err) } // Mumble config mumbleParsedURL, err := url.Parse(os.Getenv("MUMBLE_URL")) if err != nil { log.Fatal(err) } if mumbleParsedURL.User.Username() == "" { log.Fatal("Mumble URL must include a username") } mumbleConf := gumble.NewConfig() if !strings.ContainsRune(mumbleParsedURL.Host, ':') { // If address does not specify port... mumbleConf.Address = mumbleParsedURL.Host + ":64738" } else { mumbleConf.Address = mumbleParsedURL.Host } mumbleConf.Username = mumbleParsedURL.User.Username() if pass, ok := mumbleParsedURL.User.Password(); ok { mumbleConf.Password = pass } client := gumble.NewClient(mumbleConf) client.Attach(gumbleutil.Listener{ UserChange: func(e *gumble.UserChangeEvent) { if e.Type.Has(gumble.UserChangeConnected) { fmt.Println("User connected!") sendToChat(bot, fmt.Sprintf("%s connected #cgsnotify", e.User.Name)) } }, Disconnect: func(e *gumble.DisconnectEvent) { switch { case e.Type.Has(gumble.DisconnectError): sendToChat(bot, "Disconnected from mumble due to error, reconnecting in 5s") time.Sleep(5 * time.Second) e.Client.Connect() case e.Type.Has(gumble.DisconnectKicked): sendToChat(bot, "I just got kicked from mumble - rejoining out of spite") e.Client.Connect() case e.Type.Has(gumble.DisconnectBanned): sendToChat(bot, "I just got banned from mumble! Rude!") } }, }) if err := client.Connect(); err != nil { panic(err) } for update := range updates { log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) switch update.Message.Text { case "/users": var users []string for _, user := range client.Users { if user.Session != client.Self.Session { // Skip adding ourself to the online users users = append(users, user.Name) } } if len(users) == 0 { users = []string{"Noone"} } var isare string if len(users) < 2 { isare = "is" } else { isare = "are" } bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("%s %s online", prettyListify(users), isare))) case "/chatid": bot.Send(tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("This chat's ID: %d", update.Message.Chat.ID))) } } }
// connection test func TestMain(t *testing.T) { var err error address = os.Getenv("FUMBLE_ADDRESS") username = os.Getenv("FUMBLE_USERNAME") password = os.Getenv("FUMBLE_PASSWORD") // insecure ins := os.Getenv("FUMBLE_INSECURE") if ins == "" { ins = "false" } insecure, err = strconv.ParseBool(ins) if err != nil { log.Fatal(err) } // end: insecure // certificate certificateFile = os.Getenv("FUMBLE_CERTIFICATE") keyFile = os.Getenv("FUMBLE_KEY") // end: certificate // rpc address rpc_address = os.Getenv("FUMBLE_TEST_RPC_ADDRESS") if rpc_address == "" { rpc_address = "localhost:7070" } // end: rpc address config := gumble.NewConfig() config.Address = address config.Username = username config.Password = password if insecure { config.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 { config.TLSConfig.Certificates = append(config.TLSConfig.Certificates, certificate) } } bot.Config = config bot_config = bot.Config bot.create() go bot.Connect() client, err = rpc.DialHTTP("tcp", rpc_address) if err != nil { log.Fatal("dialing:", err) } assert.Nil(t, err) assert.NotNil(t, client) }
// 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 }