package config import ( "fmt" "git.stormux.org/storm/barnard/uiterm" "gopkg.in/yaml.v2" "git.stormux.org/storm/barnard/gumble/gumble" "io/ioutil" "os" "os/user" "strconv" "strings" ) type Config struct { config *exportableConfig fn string } type exportableConfig struct { Hotkeys *Hotkeys MicVolume *float32 InputDevice *string OutputDevice *string Servers []*server DefaultServer *string Username *string NotifyCommand *string } type server struct { Host string Port int Users []*eUser } type eUser struct { Username string Boost uint16 Volume float32 LocallyMuted bool // Changed from Muted to LocallyMuted to match User struct } func (c *Config) SaveConfig() { var data []byte data, err := yaml.Marshal(c.config) if err != nil { panic(err) } err = ioutil.WriteFile(c.fn+".tmp", data, 0600) if err != nil { panic(err) } err = os.Rename(c.fn+".tmp", c.fn) if err != nil { panic(err) } } func key(k uiterm.Key) *uiterm.Key { return &k } func (c *Config) LoadConfig() { var jc exportableConfig jc = exportableConfig{} jc.Hotkeys = &Hotkeys{ Talk: key(uiterm.KeyF1), VolumeDown: key(uiterm.KeyF5), VolumeUp: key(uiterm.KeyF6), MuteToggle: key(uiterm.KeyF7), // Added mute toggle hotkey Exit: key(uiterm.KeyF10), ToggleTimestamps: key(uiterm.KeyF3), SwitchViews: key(uiterm.KeyTab), ScrollUp: key(uiterm.KeyPgup), ScrollDown: key(uiterm.KeyPgdn), } if fileExists(c.fn) { var data []byte data = readFile(c.fn) if data != nil { err := yaml.UnmarshalStrict(data, &jc) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing \"%s\".\n%s\n", c.fn, err.Error()) os.Exit(1) } } } c.config = &jc if c.config.MicVolume == nil { micvol := float32(1.0) jc.MicVolume = &micvol } if c.config.InputDevice == nil { idev := string("") jc.InputDevice = &idev } if c.config.OutputDevice == nil { odev := string("") jc.OutputDevice = &odev } if c.config.DefaultServer == nil { defaultServer := string("localhost:64738") jc.DefaultServer = &defaultServer } if c.config.Username == nil { username := string("") jc.Username = &username } if c.config.NotifyCommand == nil { ncmd := string("") jc.NotifyCommand = &ncmd } } func (c *Config) findServer(address string) *server { if c.config.Servers == nil { c.config.Servers = make([]*server, 0) } host, port := makeHostPort(address) var t *server for _, s := range c.config.Servers { if s.Port == port && s.Host == host { t = s break } } if t == nil { t = &server{ Host: host, Port: port, } c.config.Servers = append(c.config.Servers, t) } return t } func (c *Config) findUser(address string, username string) *eUser { var s *server s = c.findServer(address) if s.Users == nil { s.Users = make([]*eUser, 0) } var t *eUser for _, u := range s.Users { if u.Username == username { t = u break } } if t == nil { t = &eUser{ Username: username, Boost: uint16(1), Volume: 1.0, LocallyMuted: false, // Initialize local mute state } s.Users = append(s.Users, t) } return t } func (c *Config) ToggleMute(u *gumble.User) { j := c.findUser(u.GetClient().Config.Address, u.Name) j.LocallyMuted = !j.LocallyMuted u.LocallyMuted = j.LocallyMuted c.SaveConfig() } func (c *Config) SetMicVolume(v float32) { t := float32(v) c.config.MicVolume = &t } func (c *Config) GetHotkeys() *Hotkeys { return c.config.Hotkeys } func (c *Config) GetNotifyCommand() *string { return c.config.NotifyCommand } func (c *Config) GetInputDevice() *string { return c.config.InputDevice } func (c *Config) GetOutputDevice() *string { return c.config.OutputDevice } func (c *Config) GetDefaultServer() *string { return c.config.DefaultServer } func (c *Config) GetUsername() *string { return c.config.Username } func (c *Config) UpdateUser(u *gumble.User) { var j *eUser var uc *gumble.Client uc = u.GetClient() if uc != nil { j = c.findUser(uc.Config.Address, u.Name) u.Boost = j.Boost u.Volume = j.Volume u.LocallyMuted = j.LocallyMuted // Update LocallyMuted state from config if u.Boost < 1 { u.Boost = 1 } } } func (c *Config) UpdateConfig(u *gumble.User) { var j *eUser j = c.findUser(u.GetClient().Config.Address, u.Name) j.Boost = u.Boost j.Volume = u.Volume j.LocallyMuted = u.LocallyMuted // Save LocallyMuted state to config } func NewConfig(fn *string) *Config { var c *Config c = &Config{} c.fn = resolvePath(*fn) c.LoadConfig() return c } func readFile(path string) []byte { if !fileExists(path) { return nil } dat, err := ioutil.ReadFile(path) if err != nil { return nil } return dat } func fileExists(path string) bool { info, err := os.Stat(path) if os.IsNotExist(err) { return false } return !info.IsDir() } func resolvePath(path string) string { if strings.HasPrefix(path, "~/") || strings.Contains(path, "$HOME") { usr, err := user.Current() if err != nil { panic(err) } var hd = usr.HomeDir if strings.Contains(path, "$HOME") { path = strings.Replace(path, "$HOME", hd, 1) } else { path = strings.Replace(path, "~", hd, 1) } } return path } func makeHostPort(addr string) (string, int) { parts := strings.Split(addr, ":") host := parts[0] port, err := strconv.Atoi(parts[1]) if err != nil { panic(err) } return host, port } func Log(s string) { log(s) } func log(s string) { s += "\n" f, err := os.OpenFile("log.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { panic(err) } if _, err := f.Write([]byte(s)); err != nil { panic(err) } if err := f.Close(); err != nil { panic(err) } }