diff --git a/cmdConfig.go b/cmdConfig.go new file mode 100644 index 0000000..64a14ee --- /dev/null +++ b/cmdConfig.go @@ -0,0 +1,84 @@ +// +build !rm_basic_commands allcommands setcmd + +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/BurntSushi/toml" +) + +func init() { + command := Command{ + Cmd: []string{"config"}, + Description: "Change various settings", + Help: "", + Exec: cmdConfig, + } + + RegisterCommand(command) +} + +func cmdConfig(cmd []string) { + var err error + switch { + case len(cmd) == 2: + if cmd[1] == "load" { + config, err = readConfig() + if err != nil { + printError(err.Error()) + return + } + printInfoF("Config file loaded: $TEXT", config.Colors.Message.Attachment.stylize(config.filepath)) + return + } + case len(cmd) > 2: + if cmd[1] == "load" { + config, err = readConfig(cmd[3]) + if err != nil { + printError(err.Error()) + return + } + printInfoF("Config file loaded: $TEXT", config.Colors.Message.Attachment.stylize(config.filepath)) + return + } + } + printError("Must pass a valid command") +} + +func readConfig(filepath ...string) (*Config, error) { + var result = new(Config) + var configFile string + var env bool + + switch len(filepath) { + case 0: + configFile, env = os.LookupEnv("KBTUI_CFG") + if !env { + configFile = "~/.config/kbtui.toml" + if _, err := os.Stat(configFile); os.IsNotExist(err) { + configFile = "kbtui.toml" + } + } + default: + configFile = filepath[0] + if _, err := os.Stat(configFile); os.IsNotExist(err) { + return result, fmt.Errorf("Unable to load config: %s not found", configFile) + } + } + + f, err := ioutil.ReadFile(configFile) + if err != nil { + f = []byte(defaultConfig) + } + + err = toml.Unmarshal(f, result) + if err != nil { + return result, err + } + + result.filepath = configFile + return result, nil +} diff --git a/cmdDownload.go b/cmdDownload.go index bda6233..c78df27 100644 --- a/cmdDownload.go +++ b/cmdDownload.go @@ -21,7 +21,7 @@ func init() { func cmdDownloadFile(cmd []string) { if len(cmd) < 2 { - printInfo(fmt.Sprintf("%s%s $messageId $fileName - Download a file to user's downloadpath", cmdPrefix, cmd[0])) + printInfo(fmt.Sprintf("%s%s $messageId $fileName - Download a file to user's downloadpath", config.Basics.CmdPrefix, cmd[0])) return } messageID, err := strconv.Atoi(cmd[1]) @@ -46,8 +46,8 @@ func cmdDownloadFile(cmd []string) { fileName = api.Result.Messages[0].Msg.Content.Attachment.Object.Filename } - _, err = chat.Download(messageID, fmt.Sprintf("%s/%s", downloadPath, fileName)) - channelName := messageLinkKeybaseColor.stylize(channel.Name) + _, err = chat.Download(messageID, fmt.Sprintf("%s/%s", config.Basics.DownloadPath, fileName)) + channelName := config.Colors.Message.LinkKeybase.stylize(channel.Name) if err != nil { printErrorF(fmt.Sprintf("There was an error downloading %s from $TEXT", fileName), channelName) } else { diff --git a/cmdHelp.go b/cmdHelp.go index bbf71de..f7574bb 100644 --- a/cmdHelp.go +++ b/cmdHelp.go @@ -25,7 +25,7 @@ func cmdHelp(cmd []string) { if len(cmd) == 1 { sort.Strings(baseCommands) for _, c := range baseCommands { - helpText = fmt.Sprintf("%s%s%s\t\t%s\n", helpText, cmdPrefix, c, commands[c].Description) + helpText = fmt.Sprintf("%s%s%s\t\t%s\n", helpText, config.Basics.CmdPrefix, c, commands[c].Description) } if len(typeCommands) > 0 { for c := range typeCommands { diff --git a/cmdJoin.go b/cmdJoin.go index 05858c7..a2800d1 100644 --- a/cmdJoin.go +++ b/cmdJoin.go @@ -41,12 +41,12 @@ func cmdJoin(cmd []string) { channel.TopicName = "" channel.MembersType = keybase.USER } - printInfoF("You are joining: $TEXT", messageLinkKeybaseColor.stylize(joinedName)) + printInfoF("You are joining: $TEXT", config.Colors.Message.LinkKeybase.stylize(joinedName)) clearView("Chat") setViewTitle("Input", fmt.Sprintf(" %s ", joinedName)) go populateChat() default: - printInfo(fmt.Sprintf("To join a team use %sjoin ", cmdPrefix)) - printInfo(fmt.Sprintf("To join a PM use %sjoin ", cmdPrefix)) + printInfo(fmt.Sprintf("To join a team use %sjoin ", config.Basics.CmdPrefix)) + printInfo(fmt.Sprintf("To join a PM use %sjoin ", config.Basics.CmdPrefix)) } } diff --git a/cmdReply.go b/cmdReply.go index 9a21aa3..b47f60b 100644 --- a/cmdReply.go +++ b/cmdReply.go @@ -22,7 +22,7 @@ func init() { func cmdReply(cmd []string) { chat := k.NewChat(channel) if len(cmd) < 2 { - printInfo(fmt.Sprintf("%s%s $ID - Reply to message $ID", cmdPrefix, cmd[0])) + printInfo(fmt.Sprintf("%s%s $ID - Reply to message $ID", config.Basics.CmdPrefix, cmd[0])) return } messageID, err := strconv.Atoi(cmd[1]) diff --git a/cmdSet.go b/cmdSet.go deleted file mode 100644 index 812fb48..0000000 --- a/cmdSet.go +++ /dev/null @@ -1,125 +0,0 @@ -// +build !rm_basic_commands allcommands setcmd - -package main - -import ( - "fmt" - "os" - "strings" - - "github.com/pelletier/go-toml" -) - -func init() { - command := Command{ - Cmd: []string{"set", "config"}, - Description: "Change various settings", - Help: "", - Exec: cmdSet, - } - - RegisterCommand(command) -} -func printSetting(cmd []string) { - switch cmd[1] { - case "load": - loadFromToml() - case "downloadPath": - printInfo(fmt.Sprintf("Setting for %s -> %s", cmd[1], downloadPath)) - case "outputFormat": - printInfo(fmt.Sprintf("Setting for %s -> %s", cmd[1], outputFormat)) - case "dateFormat": - printInfo(fmt.Sprintf("Setting for %s -> %s", cmd[1], dateFormat)) - case "timeFormat": - printInfo(fmt.Sprintf("Setting for %s -> %s", cmd[1], timeFormat)) - case "cmdPrefix": - printInfo(fmt.Sprintf("Setting for %s -> %s", cmd[1], cmdPrefix)) - default: - printError(fmt.Sprintf("Unknown config value %s", cmd[1])) - } -} -func cmdSet(cmd []string) { - if len(cmd) < 2 { - printError("No config value specified") - return - } - if len(cmd) < 3 { - printSetting(cmd) - return - } - switch cmd[1] { - case "downloadPath": - if len(cmd) != 3 { - printError("Invalid download path.") - } - downloadPath = cmd[2] - case "outputFormat": - outputFormat = strings.Join(cmd[1:], " ") - case "dateFormat": - dateFormat = strings.Join(cmd[1:], " ") - case "timeFormat": - timeFormat = strings.Join(cmd[1:], " ") - case "cmdPrefix": - cmdPrefix = cmd[2] - default: - printError(fmt.Sprintf("Unknown config value %s", cmd[1])) - } - -} -func loadFromToml() { - configFile, env := os.LookupEnv("KBTUI_CFG") - if !env { - configFile = "~/.config/kbtui.toml" - if _, err := os.Stat(configFile); os.IsNotExist(err) { - configFile = "kbtui.toml" - } - - } - printInfoF("Loading config from toml: $TEXT", messageAttachmentColor.stylize(configFile)) - config, err := toml.LoadFile(configFile) - if err != nil { - printError(fmt.Sprintf("Could not read config file: %+v", err)) - return - } - colorless = config.GetDefault("Basics.colorless", false).(bool) - if config.Has("Basics.colorless") { - colorless = config.Get("Basics.colorless").(bool) - } - if config.Has("Basics.downloadPath") { - downloadPath = config.Get("Basics.downloadPath").(string) - } - if config.Has("Basics.cmdPrefix") { - cmdPrefix = config.Get("Basics.cmdPrefix").(string) - } - if config.Has("Formatting.outputFormat") { - outputFormat = config.Get("Formatting.outputFormat").(string) - } - if config.Has("Formatting.dateFormat") { - dateFormat = config.Get("Formatting.dateFormat").(string) - } - if config.Has("Formatting.timeFormat") { - timeFormat = config.Get("Formatting.timeFormat").(string) - } - channelsColor = styleFromConfig(config, "channels.basic") - - channelsHeaderColor = styleFromConfig(config, "channels.header") - channelUnreadColor = styleFromConfig(config, "channels.unread") - - mentionColor = styleFromConfig(config, "message.mention") - messageHeaderColor = styleFromConfig(config, "message.header") - messageIDColor = styleFromConfig(config, "message.id") - messageTimeColor = styleFromConfig(config, "message.time") - messageSenderDefaultColor = styleFromConfig(config, "message.sender_default") - messageSenderDeviceColor = styleFromConfig(config, "message.sender_device") - messageBodyColor = styleFromConfig(config, "message.body") - messageAttachmentColor = styleFromConfig(config, "message.attachment") - messageLinkURLColor = styleFromConfig(config, "message.link_url") - messageLinkKeybaseColor = styleFromConfig(config, "message.link_keybase") - messageReactionColor = styleFromConfig(config, "message.reaction") - messageCodeColor = styleFromConfig(config, "message.code") - - feedColor = styleFromConfig(config, "feed.basic") - errorColor = styleFromConfig(config, "feed.error") - - RunCommand("clean") -} diff --git a/cmdStream.go b/cmdStream.go index 4e8b848..b5a57ae 100644 --- a/cmdStream.go +++ b/cmdStream.go @@ -22,6 +22,6 @@ func cmdStream(cmd []string) { channel.Name = "" printInfo("You are now viewing the formatted stream") - setViewTitle("Input", fmt.Sprintf(" Stream - Not in a chat. %sj to join ", cmdPrefix)) + setViewTitle("Input", fmt.Sprintf(" Stream - Not in a chat. %sj to join ", config.Basics.CmdPrefix)) clearView("Chat") } diff --git a/cmdUploadFile.go b/cmdUploadFile.go index e6f61ec..07d05a8 100644 --- a/cmdUploadFile.go +++ b/cmdUploadFile.go @@ -21,7 +21,7 @@ func init() { func cmdUploadFile(cmd []string) { if len(cmd) < 2 { - printInfo(fmt.Sprintf("%s%s $filePath $fileName - Upload file from absolute path with optional name", cmdPrefix, cmd[0])) + printInfo(fmt.Sprintf("%s%s $filePath $fileName - Upload file from absolute path with optional name", config.Basics.CmdPrefix, cmd[0])) return } filePath := cmd[1] @@ -40,7 +40,7 @@ func cmdUploadFile(cmd []string) { } chat := k.NewChat(channel) _, err := chat.Upload(fileName, filePath) - channelName := messageLinkKeybaseColor.stylize(channel.Name).string() + channelName := config.Colors.Message.LinkKeybase.stylize(channel.Name).string() if err != nil { printError(fmt.Sprintf("There was an error uploading %s to %s\n%+v", filePath, channelName, err)) } else { diff --git a/cmdWall.go b/cmdWall.go index 7fe0904..51f27b4 100644 --- a/cmdWall.go +++ b/cmdWall.go @@ -65,7 +65,7 @@ func cmdPopulateWall(cmd []string) { return } - printInfoF("Displaying public messages for user $TEXT", messageLinkKeybaseColor.stylize(requestedUsers)) + printInfoF("Displaying public messages for user $TEXT", config.Colors.Message.LinkKeybase.stylize(requestedUsers)) for _, chann := range users { chat := k.NewChat(chann) api, err := chat.Read() diff --git a/colors.go b/colors.go index 1c1dc44..8ff9ad0 100644 --- a/colors.go +++ b/colors.go @@ -2,16 +2,12 @@ package main import ( "fmt" - "github.com/pelletier/go-toml" "regexp" "strings" ) -// Begin Colors -type color int - const ( - black color = iota + black int = iota red green yellow @@ -19,103 +15,88 @@ const ( magenta cyan grey - normal color = -1 + normal int = -1 ) -func colorFromString(s string) color { - s = strings.ToLower(s) - switch s { - case "black": - return black - case "red": - return red - case "green": - return green - case "yellow": - return yellow - case "purple": - return purple - case "magenta": - return magenta - case "cyan": - return cyan - case "grey": - return grey - case "normal": +var colorMapString = map[string]int{ + "black": black, + "red": red, + "green": green, + "yellow": yellow, + "purple": purple, + "magenta": magenta, + "cyan": cyan, + "grey": grey, + "normal": normal, +} + +var colorMapInt = map[int]string{ + black: "black", + red: "red", + green: "green", + yellow: "yellow", + purple: "purple", + magenta: "magenta", + cyan: "cyan", + grey: "grey", + normal: "normal", +} + +func colorFromString(color string) int { + var result int + color = strings.ToLower(color) + result, ok := colorMapString[color] + if !ok { return normal - default: - printError(fmt.Sprintf("color `%s` cannot be parsed.", s)) } - return normal -} - -// Style struct for specializing the style/color of a stylize -type Style struct { - foregroundColor color - backgroundColor color - bold bool - italic bool // Currently not supported by the UI library - underline bool - strikethrough bool // Currently not supported by the UI library - inverse bool + return result } -var basicStyle = Style{normal, normal, false, false, false, false, false} - -func styleFromConfig(config *toml.Tree, key string) Style { - key = "Colors." + key + "." - style := basicStyle - if config.Has(key + "foreground") { - style = style.withForeground(colorFromString(config.Get(key + "foreground").(string))) - } - if config.Has(key + "background") { - style = style.withForeground(colorFromString(config.Get(key + "background").(string))) - } - if config.GetDefault(key+"bold", false).(bool) { - style = style.withBold() - } - if config.GetDefault(key+"italic", false).(bool) { - style = style.withItalic() - } - if config.GetDefault(key+"underline", false).(bool) { - style = style.withUnderline() - } - if config.GetDefault(key+"strikethrough", false).(bool) { - style = style.withStrikethrough() - } - if config.GetDefault(key+"inverse", false).(bool) { - style = style.withInverse() +func colorFromInt(color int) string { + var result string + result, ok := colorMapInt[color] + if !ok { + return "normal" } + return result +} - return style +var basicStyle = Style{ + Foreground: colorMapInt[normal], + Background: colorMapInt[normal], + Italic: false, + Bold: false, + Underline: false, + Strikethrough: false, + Inverse: false, } -func (s Style) withForeground(f color) Style { - s.foregroundColor = f +func (s Style) withForeground(color int) Style { + s.Foreground = colorFromInt(color) return s } -func (s Style) withBackground(f color) Style { - s.backgroundColor = f +func (s Style) withBackground(color int) Style { + s.Background = colorFromInt(color) return s } func (s Style) withBold() Style { - s.bold = true + s.Bold = true return s } func (s Style) withInverse() Style { - s.inverse = true + s.Inverse = true return s } func (s Style) withItalic() Style { - s.italic = true + s.Italic = true return s } func (s Style) withStrikethrough() Style { - s.strikethrough = true + s.Strikethrough = true return s } func (s Style) withUnderline() Style { - s.underline = true + s.Underline = true return s } @@ -123,29 +104,29 @@ func (s Style) withUnderline() Style { // which essentially just adds on top. that is relevant in the case of // bold/italic etc - it should add style - not clear. func (s Style) toANSI() string { - if colorless { + if config.Basics.Colorless { return "" } output := "\x1b[0m\x1b[0" - if s.foregroundColor != normal { - output += fmt.Sprintf(";%d", 30+s.foregroundColor) + if colorFromString(s.Foreground) != normal { + output += fmt.Sprintf(";%d", 30+colorFromString(s.Foreground)) } - if s.backgroundColor != normal { - output += fmt.Sprintf(";%d", 40+s.backgroundColor) + if colorFromString(s.Background) != normal { + output += fmt.Sprintf(";%d", 40+colorFromString(s.Background)) } - if s.bold { + if s.Bold { output += ";1" } - if s.italic { + if s.Italic { output += ";3" } - if s.underline { + if s.Underline { output += ";4" } - if s.inverse { + if s.Inverse { output += ";7" } - if s.strikethrough { + if s.Strikethrough { output += ";9" } diff --git a/defaultConfig.go b/defaultConfig.go new file mode 100644 index 0000000..4d406a5 --- /dev/null +++ b/defaultConfig.go @@ -0,0 +1,70 @@ +package main + +var defaultConfig = ` +[basics] +downloadPath = "/tmp/" +colorless = false +# The prefix before evaluating a command +cmdPrefix = "/" + +[formatting] +# BASH-like PS1 variable equivalent +outputFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +outputStreamFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +outputMentionFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +pmFormat = "PM from $USER@$DEVICE: $MSG" + +# 02 = Day, Jan = Month, 06 = Year +dateFormat = "02Jan06" + +# 15 = hours, 04 = minutes, 05 = seconds +timeFormat = "15:04" + + +[colors] + [colors.channels] + [colors.channels.basic] + foreground = "normal" + [colors.channels.header] + foreground = "magenta" + bold = true + [colors.channels.unread] + foreground = "green" + italic = true + + [colors.message] + [colors.message.body] + foreground = "normal" + [colors.message.header] + foreground = "grey" + [colors.message.mention] + foreground = "green" + italic = true + bold = true + [colors.message.id] + foreground = "yellow" + [colors.message.time] + foreground = "magenta" + [colors.message.sender_default] + foreground = "cyan" + bold = true + [colors.message.sender_device] + foreground = "cyan" + [colors.message.attachment] + foreground = "red" + [colors.message.link_url] + foreground = "yellow" + [colors.message.link_keybase] + foreground = "yellow" + [colors.message.reaction] + foreground = "magenta" + bold = true + [colors.message.code] + foreground = "cyan" + background = "grey" + [colors.feed] + [colors.feed.basic] + foreground = "grey" + [colors.feed.error] + foreground = "red" +` diff --git a/kbtui.toml b/kbtui.toml index 37a5af4..290b742 100644 --- a/kbtui.toml +++ b/kbtui.toml @@ -1,12 +1,15 @@ -[Basics] +[basics] downloadPath = "/tmp/" colorless = false # The prefix before evaluating a command cmdPrefix = "/" -[Formatting] +[formatting] # BASH-like PS1 variable equivalent outputFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +outputStreamFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +outputMentionFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" +pmFormat = "PM from $USER@$DEVICE: $MSG" # 02 = Day, Jan = Month, 06 = Year dateFormat = "02Jan06" @@ -15,49 +18,49 @@ dateFormat = "02Jan06" timeFormat = "15:04" -[Colors] - [Colors.channels] - [Colors.channels.basic] +[colors] + [colors.channels] + [colors.channels.basic] foreground = "normal" - [Colors.channels.header] + [colors.channels.header] foreground = "magenta" bold = true - [Colors.channels.unread] + [colors.channels.unread] foreground = "green" italic = true - [Colors.message] - [Colors.message.body] + [colors.message] + [colors.message.body] foreground = "normal" - [Colors.message.header] + [colors.message.header] foreground = "grey" - [Colors.message.mention] + [colors.message.mention] foreground = "green" italic = true bold = true - [Colors.message.id] + [colors.message.id] foreground = "yellow" - [Colors.message.time] + [colors.message.time] foreground = "magenta" - [Colors.message.sender_default] + [colors.message.sender_default] foreground = "cyan" bold = true - [Colors.message.sender_device] + [colors.message.sender_device] foreground = "cyan" - [Colors.message.attachment] + [colors.message.attachment] foreground = "red" - [Colors.message.link_url] + [colors.message.link_url] foreground = "yellow" - [Colors.message.link_keybase] + [colors.message.link_keybase] foreground = "yellow" - [Colors.message.reaction] + [colors.message.reaction] foreground = "magenta" bold = true - [Colors.message.code] + [colors.message.code] foreground = "cyan" background = "grey" - [Colors.feed] - [Colors.feed.basic] + [colors.feed] + [colors.feed.basic] foreground = "grey" - [Colors.feed.error] + [colors.feed.error] foreground = "red" \ No newline at end of file diff --git a/main.go b/main.go index d83ecbc..16e1dec 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ var ( g *gocui.Gui ) +var config *Config + func main() { if !k.LoggedIn { fmt.Println("You are not logged in.") @@ -37,7 +39,7 @@ func main() { } defer g.Close() g.SetManagerFunc(layout) - go RunCommand("config", "load") + RunCommand("config", "load") go populateList() go updateChatWindow() if len(os.Args) > 1 { @@ -82,7 +84,7 @@ func layout(g *gocui.Gui) error { chatView.Autoscroll = true chatView.Wrap = true welcomeText := basicStyle.stylize("Welcome $USER!\n\nYour chats will appear here.\nSupported commands are as follows:\n") - welcomeText = welcomeText.replace("$USER", mentionColor.stylize(k.Username)) + welcomeText = welcomeText.replace("$USER", config.Colors.Message.Mention.stylize(k.Username)) fmt.Fprintln(chatView, welcomeText.string()) RunCommand("help") } @@ -95,7 +97,7 @@ func layout(g *gocui.Gui) error { } inputView.Editable = true inputView.Wrap = true - inputView.Title = fmt.Sprintf(" Not in a chat - write `%sj` to join", cmdPrefix) + inputView.Title = fmt.Sprintf(" Not in a chat - write `%sj` to join", config.Basics.CmdPrefix) g.Cursor = true } if listView, err4 := g.SetView("List", 0, 0, maxX/2-maxX/3-1, maxY-1, 0); err4 != nil { @@ -255,7 +257,7 @@ func printError(message string) { printErrorF(message) } func printErrorF(message string, parts ...StyledString) { - printToView("Feed", errorColor.sprintf(removeFormatting(message), parts...).string()) + printToView("Feed", config.Colors.Feed.Error.sprintf(removeFormatting(message), parts...).string()) } // this removes formatting @@ -265,7 +267,7 @@ func printInfo(message string) { // this removes formatting func printInfoF(message string, parts ...StyledString) { - printToView("Feed", feedColor.sprintf(removeFormatting(message), parts...).string()) + printToView("Feed", config.Colors.Feed.Basic.sprintf(removeFormatting(message), parts...).string()) } func printToView(viewName string, message string) { g.Update(func(g *gocui.Gui) error { @@ -345,10 +347,10 @@ func populateList() { log.Printf("%+v", err) } else { clearView("List") - var textBase = channelsColor.stylize("") - var recentPMs = textBase.append(channelsHeaderColor.stylize("---[PMs]---\n")) + var textBase = config.Colors.Channels.Basic.stylize("") + var recentPMs = textBase.append(config.Colors.Channels.Header.stylize("---[PMs]---\n")) var recentPMsCount = 0 - var recentChannels = textBase.append(channelsHeaderColor.stylize("---[Teams]---\n")) + var recentChannels = textBase.append(config.Colors.Channels.Header.stylize("---[Teams]---\n")) var recentChannelsCount = 0 for _, s := range testVar.Result.Conversations { channels = append(channels, s.Channel) @@ -357,7 +359,7 @@ func populateList() { if recentChannelsCount <= ((maxY - 2) / 3) { channel := fmt.Sprintf("%s\n\t#%s\n", s.Channel.Name, s.Channel.TopicName) if s.Unread { - recentChannels = recentChannels.append(channelUnreadColor.stylize("*" + channel)) + recentChannels = recentChannels.append(config.Colors.Channels.Unread.stylize("*" + channel)) } else { recentChannels = recentChannels.appendString(channel) } @@ -367,7 +369,7 @@ func populateList() { if recentPMsCount <= ((maxY - 2) / 3) { pmName := fmt.Sprintf("%s\n", cleanChannelName(s.Channel.Name)) if s.Unread { - recentPMs = recentPMs.append(channelUnreadColor.stylize("*" + pmName)) + recentPMs = recentPMs.append(config.Colors.Channels.Unread.stylize("*" + pmName)) } else { recentPMs = recentPMs.appendString(pmName) } @@ -384,35 +386,35 @@ func populateList() { // Formatting func formatMessageBody(body string) StyledString { - output := messageBodyColor.stylize(body) + output := config.Colors.Message.Body.stylize(body) output = colorReplaceMentionMe(output) - output = output.colorRegex(`_[^_]*_`, messageBodyColor.withItalic()) - output = output.colorRegex(`~[^~]*~`, messageBodyColor.withStrikethrough()) - output = output.colorRegex(`@[\w_]*(\.[\w_]+)*`, messageLinkKeybaseColor) + output = output.colorRegex(`_[^_]*_`, config.Colors.Message.Body.withItalic()) + output = output.colorRegex(`~[^~]*~`, config.Colors.Message.Body.withStrikethrough()) + output = output.colorRegex(`@[\w_]*(\.[\w_]+)*`, config.Colors.Message.LinkKeybase) // TODO change how bold, italic etc works, so it uses boldOn boldOff ([1m and [22m) - output = output.colorRegex(`\*[^\*]*\*`, messageBodyColor.withBold()) + output = output.colorRegex(`\*[^\*]*\*`, config.Colors.Message.Body.withBold()) output = output.replaceString("```", "") // TODO make background color cover whole line - output = output.colorRegex("(.*\n)*", messageCodeColor) - output = output.colorRegex("`[^`]*`", messageCodeColor) + output = output.colorRegex("(.*\n)*", config.Colors.Message.Code) + output = output.colorRegex("`[^`]*`", config.Colors.Message.Code) // mention URL - output = output.colorRegex(`(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))`, messageLinkURLColor) + output = output.colorRegex(`(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))`, config.Colors.Message.LinkURL) return output } // TODO use this more func formatChannel(ch keybase.Channel) StyledString { - return messageLinkKeybaseColor.stylize(fmt.Sprintf("@%s#%s", ch.Name, ch.TopicName)) + return config.Colors.Message.LinkKeybase.stylize(fmt.Sprintf("@%s#%s", ch.Name, ch.TopicName)) } func colorReplaceMentionMe(msg StyledString) StyledString { - return msg.colorRegex("(@?"+k.Username+")", mentionColor) + return msg.colorRegex("(@?"+k.Username+")", config.Colors.Message.Mention) } func colorUsername(username string) StyledString { - var color = messageSenderDefaultColor + var color = config.Colors.Message.SenderDefault if username == k.Username { - color = mentionColor + color = config.Colors.Message.Mention } return color.stylize(username) } @@ -423,27 +425,27 @@ func cleanChannelName(c string) string { } func formatMessage(api keybase.ChatAPI, formatString string) string { - ret := messageHeaderColor.stylize("") + ret := config.Colors.Message.Header.stylize("") msgType := api.Msg.Content.Type switch msgType { case "text", "attachment": - ret = messageHeaderColor.stylize(formatString) + ret = config.Colors.Message.Header.stylize(formatString) tm := time.Unix(int64(api.Msg.SentAt), 0) var msg = formatMessageBody(api.Msg.Content.Text.Body) if msgType == "attachment" { - msg = messageBodyColor.stylize("$TITLE\n$FILE") + msg = config.Colors.Message.Body.stylize("$TITLE\n$FILE") attachment := api.Msg.Content.Attachment msg = msg.replaceString("$TITLE", attachment.Object.Title) - msg = msg.replace("$FILE", messageAttachmentColor.stylize(fmt.Sprintf("[Attachment: %s]", attachment.Object.Filename))) + msg = msg.replace("$FILE", config.Colors.Message.Attachment.stylize(fmt.Sprintf("[Attachment: %s]", attachment.Object.Filename))) } user := colorUsername(api.Msg.Sender.Username) - device := messageSenderDeviceColor.stylize(api.Msg.Sender.DeviceName) - msgID := messageIDColor.stylize(fmt.Sprintf("%d", api.Msg.ID)) - date := messageTimeColor.stylize(tm.Format(dateFormat)) - msgTime := messageTimeColor.stylize(tm.Format(timeFormat)) + device := config.Colors.Message.SenderDevice.stylize(api.Msg.Sender.DeviceName) + msgID := config.Colors.Message.ID.stylize(fmt.Sprintf("%d", api.Msg.ID)) + date := config.Colors.Message.Time.stylize(tm.Format(config.Formatting.DateFormat)) + msgTime := config.Colors.Message.Time.stylize(tm.Format(config.Formatting.TimeFormat)) - channelName := messageIDColor.stylize(fmt.Sprintf("@%s#%s", api.Msg.Channel.Name, api.Msg.Channel.TopicName)) + channelName := config.Colors.Message.ID.stylize(fmt.Sprintf("@%s#%s", api.Msg.Channel.Name, api.Msg.Channel.TopicName)) ret = ret.replace("$MSG", msg) ret = ret.replace("$USER", user) ret = ret.replace("$DEVICE", device) @@ -455,9 +457,9 @@ func formatMessage(api keybase.ChatAPI, formatString string) string { return ret.string() } func formatOutput(api keybase.ChatAPI) string { - format := outputFormat + format := config.Formatting.OutputFormat if stream { - format = outputStreamFormat + format = config.Formatting.OutputStreamFormat } return formatMessage(api, format) } @@ -485,7 +487,7 @@ func handleMessage(api keybase.ChatAPI) { if m.Text == k.Username { // We are in a team if topicName != channel.TopicName { - printInfo(formatMessage(api, mentionFormat)) + printInfo(formatMessage(api, config.Formatting.OutputMentionFormat)) fmt.Print("\a") } @@ -494,7 +496,7 @@ func handleMessage(api keybase.ChatAPI) { } } else { if msgSender != channel.Name { - printInfo(formatMessage(api, pmFormat)) + printInfo(formatMessage(api, config.Formatting.PMFormat)) fmt.Print("\a") } @@ -512,7 +514,7 @@ func handleMessage(api keybase.ChatAPI) { if api.Msg.Channel.MembersType == keybase.TEAM { printToView("Chat", formatOutput(api)) } else { - printToView("Chat", formatMessage(api, pmFormat)) + printToView("Chat", formatMessage(api, config.Formatting.PMFormat)) } } } else { @@ -546,8 +548,8 @@ func handleInput(viewName string) error { if inputString == "" { return nil } - if strings.HasPrefix(inputString, cmdPrefix) { - cmd := deleteEmpty(strings.Split(inputString[len(cmdPrefix):], " ")) + if strings.HasPrefix(inputString, config.Basics.CmdPrefix) { + cmd := deleteEmpty(strings.Split(inputString[len(config.Basics.CmdPrefix):], " ")) if len(cmd) < 1 { return nil } diff --git a/tcmdShowReactions.go b/tcmdShowReactions.go index 5fa278f..ac3f2f6 100644 --- a/tcmdShowReactions.go +++ b/tcmdShowReactions.go @@ -22,9 +22,9 @@ func init() { func tcmdShowReactions(m keybase.ChatAPI) { team := false user := colorUsername(m.Msg.Sender.Username) - id := messageIDColor.stylize(fmt.Sprintf("%d", m.Msg.Content.Reaction.M)) - reaction := messageReactionColor.stylize(m.Msg.Content.Reaction.B) - where := messageLinkKeybaseColor.stylize("a PM") + id := config.Colors.Message.ID.stylize(fmt.Sprintf("%d", m.Msg.Content.Reaction.M)) + reaction := config.Colors.Message.Reaction.stylize(m.Msg.Content.Reaction.B) + where := config.Colors.Message.LinkKeybase.stylize("a PM") if m.Msg.Channel.MembersType == keybase.TEAM { team = true where = formatChannel(m.Msg.Channel) diff --git a/types.go b/types.go index 77784e3..b8fd72f 100644 --- a/types.go +++ b/types.go @@ -17,3 +17,68 @@ type TypeCommand struct { Description string // A short description of the command Exec func(keybase.ChatAPI) // A function that takes a raw chat message as input } + +// Config holds user-configurable values +type Config struct { + filepath string `toml:"-"` + Basics Basics `toml:"basics"` + Formatting Formatting `toml:"formatting"` + Colors Colors `toml:"colors"` +} + +type Basics struct { + DownloadPath string `toml:"downloadPath"` + Colorless bool `toml:"colorless"` + CmdPrefix string `toml:"cmdPrefix"` +} + +type Formatting struct { + OutputFormat string `toml:"outputFormat"` + OutputStreamFormat string `toml:"outputStreamFormat"` + OutputMentionFormat string `toml:"outputMentionFormat"` + PMFormat string `toml:"pmFormat"` + DateFormat string `toml:"dateFormat"` + TimeFormat string `toml:"timeFormat"` +} + +type Style struct { + Foreground string `toml:"foreground"` + Background string `toml:"background"` + Italic bool `toml:"italic"` + Bold bool `toml:"bold"` + Underline bool `toml:"underline"` + Strikethrough bool `toml:"strikethrough"` + Inverse bool `toml:"inverse"` +} + +type Channels struct { + Basic Style `toml:"basic"` + Header Style `toml:"header"` + Unread Style `toml:"unread"` +} + +type Message struct { + Body Style `toml:"body"` + Header Style `toml:"header"` + Mention Style `toml:"mention"` + ID Style `toml:"id"` + Time Style `toml:"time"` + SenderDefault Style `toml:"sender_default"` + SenderDevice Style `toml:"sender_device"` + Attachment Style `toml:"attachment"` + LinkURL Style `toml:"link_url"` + LinkKeybase Style `toml:"link_keybase"` + Reaction Style `toml:"reaction"` + Code Style `toml:"code"` +} + +type Feed struct { + Basic Style `toml:"basic"` + Error Style `toml:"error"` +} + +type Colors struct { + Channels Channels `toml:"channels"` + Message Message `toml:"message"` + Feed Feed `toml:"feed"` +} diff --git a/userConfigs.go b/userConfigs.go deleted file mode 100644 index d0c7fd7..0000000 --- a/userConfigs.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -// Path where Downloaded files will default to -var downloadPath = "/tmp/" - -var colorless bool = false -var channelsColor = basicStyle -var channelUnreadColor = channelsColor.withForeground(green).withItalic() -var channelsHeaderColor = channelsColor.withForeground(magenta).withBold() - -var mentionColor = basicStyle.withForeground(green) -var messageHeaderColor = basicStyle.withForeground(grey) -var messageIDColor = basicStyle.withForeground(yellow) -var messageTimeColor = basicStyle.withForeground(magenta) -var messageSenderDefaultColor = basicStyle.withForeground(cyan) -var messageSenderDeviceColor = messageSenderDefaultColor -var messageBodyColor = basicStyle -var messageAttachmentColor = basicStyle.withForeground(red) -var messageLinkURLColor = basicStyle.withForeground(yellow) -var messageLinkKeybaseColor = basicStyle.withForeground(yellow) -var messageReactionColor = basicStyle.withForeground(magenta) -var messageCodeColor = basicStyle.withBackground(grey).withForeground(cyan) - -var feedColor = basicStyle.withForeground(grey) -var errorColor = basicStyle.withForeground(red) - -// BASH-like PS1 variable equivalent -var outputFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" -var outputStreamFormat = "┌──[$TEAM] [$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG" -var mentionFormat = outputStreamFormat -var pmFormat = "PM from $USER@$DEVICE: $MSG" - -// 02 = Day, Jan = Month, 06 = Year -var dateFormat = "02Jan06" - -// 15 = hours, 04 = minutes, 05 = seconds -var timeFormat = "15:04" - -// The prefix before evaluating a command -var cmdPrefix = "/"