diff --git a/cmdJoin.go b/cmdJoin.go index 8d040d4..1e66613 100644 --- a/cmdJoin.go +++ b/cmdJoin.go @@ -4,8 +4,8 @@ package main import ( "fmt" - "samhofi.us/x/keybase" + "strings" ) func init() { @@ -21,23 +21,31 @@ func init() { func cmdJoin(cmd []string) { stream = false - if len(cmd) == 3 { - channel.MembersType = keybase.TEAM - channel.Name = cmd[1] - channel.TopicName = cmd[2] - printToView("Feed", fmt.Sprintf("You are joining: @%s#%s", channel.Name, channel.TopicName)) - clearView("Chat") - viewTitle("Input", fmt.Sprintf(" @%s#%s ", channel.Name, channel.TopicName)) - go populateChat() - } else if len(cmd) == 2 { - channel.MembersType = keybase.USER - channel.Name = cmd[1] - channel.TopicName = "" - printToView("Feed", fmt.Sprintf("You are joining: @%s", channel.Name)) + switch l := len(cmd); l { + case 3: + fallthrough + case 2: + // if people write it in one singular line, with a `#` + firstArgSplit := strings.Split(cmd[1], "#") + channel.Name = strings.Replace(firstArgSplit[0], "@", "", 1) + joinedName := fmt.Sprintf("@%s", channel.Name) + if l == 3 || len(firstArgSplit) == 2 { + channel.MembersType = keybase.TEAM + if l == 3 { + channel.TopicName = strings.Replace(cmd[2], "#", "", 1) + } else { + channel.TopicName = firstArgSplit[1] + } + joinedName = fmt.Sprintf("%s#%s", joinedName, channel.TopicName) + } else { + channel.TopicName = "" + channel.MembersType = keybase.USER + } + printToView("Feed", fmt.Sprintf("You are joining: %s", joinedName)) clearView("Chat") - viewTitle("Input", fmt.Sprintf(" @%s ", channel.Name)) + viewTitle("Input", fmt.Sprintf(" %s ", joinedName)) go populateChat() - } else { + default: printToView("Feed", fmt.Sprintf("To join a team use %sjoin ", cmdPrefix)) printToView("Feed", fmt.Sprintf("To join a PM use %sjoin ", cmdPrefix)) } diff --git a/colors.go b/colors.go new file mode 100644 index 0000000..3ddf629 --- /dev/null +++ b/colors.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "regexp" +) + +// TODO maybe datastructure +// BASH-like PS1 variable equivalent (without colours) +// TODO bold? cursive etc? +func color(c int) string { + if colorless { + return "" + } + if c < 0 { + return "\033[0m" + } else { + return fmt.Sprintf("\033[0;%dm", 29+c) + } +} +// TODO maybe make the text into some datastructure which remembers the color +func colorText(text string, color string, offColor string) string { + return fmt.Sprintf("%s%s%s", color, text, offColor) +} + +func colorUsername(username string, offColor string) string { + var color = messageSenderDefaultColor + if username == k.Username { + color = mentionColor + } + return colorText(username, color, offColor) +} +func colorRegex(msg string, match string, color string, offColor string) string { + var re = regexp.MustCompile(match) + return re.ReplaceAllString(msg, colorText(`$1`, color, offColor)) +} + +func colorReplaceMentionMe(msg string, offColor string) string { + //var coloredOwnName = colorText(k.Username, mentionColor, offColor) + //return strings.Replace(msg, k.Username, coloredOwnName, -1) + return colorRegex(msg, "(@?"+k.Username+")", mentionColor, offColor) +} diff --git a/main.go b/main.go index d3f7064..6c9e622 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "regexp" "strings" "time" @@ -141,28 +142,33 @@ func sendChat(message string) { } func formatOutput(api keybase.ChatAPI) string { ret := "" - if api.Msg.Content.Type == "text" { - ret = outputFormat + msgType := api.Msg.Content.Type + switch (msgType) { + case "text", "attachment": + var c = messageHeaderColor + ret = colorText(outputFormat, c, noColor) tm := time.Unix(int64(api.Msg.SentAt), 0) - ret = strings.Replace(ret, "$MSG", api.Msg.Content.Text.Body, 1) - ret = strings.Replace(ret, "$USER", api.Msg.Sender.Username, 1) - ret = strings.Replace(ret, "$DEVICE", api.Msg.Sender.DeviceName, 1) - ret = strings.Replace(ret, "$ID", fmt.Sprintf("%d", api.Msg.ID), 1) - ret = strings.Replace(ret, "$DATE", fmt.Sprintf("%s", tm.Format(dateFormat)), 1) - ret = strings.Replace(ret, "$TIME", fmt.Sprintf("%s", tm.Format(timeFormat)), 1) - ret = strings.Replace(ret, "```", fmt.Sprintf("\n\n"), 10) - } - if api.Msg.Content.Type == "attachment" { - ret = outputFormat - tm := time.Unix(int64(api.Msg.SentAt), 0) - ret = strings.Replace(ret, "$MSG", "ATTACHMENT MSG", 1) - ret = strings.Replace(ret, "$USER", api.Msg.Sender.Username, 1) - ret = strings.Replace(ret, "$DEVICE", api.Msg.Sender.DeviceName, 1) - ret = strings.Replace(ret, "$ID", fmt.Sprintf("%d", api.Msg.ID), 1) - ret = strings.Replace(ret, "$DATE", fmt.Sprintf("%s", tm.Format(dateFormat)), 1) - ret = strings.Replace(ret, "$TIME", fmt.Sprintf("%s", tm.Format(timeFormat)), 1) - } + var msg = api.Msg.Content.Text.Body + // mention teams or users + msg = colorRegex(msg, `(@\w*(\.\w+)*)`, messageLinkColor, messageBodyColor) + // mention URL + msg = colorRegex(msg, `(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))`, messageLinkColor, messageBodyColor) + msg = colorText(colorReplaceMentionMe(msg, messageBodyColor), messageBodyColor,c) + if msgType == "attachment" { + msg = fmt.Sprintf("%s\n%s", msg, colorText("[Attachment]", messageAttachmentColor, c)) + } + user := colorUsername(api.Msg.Sender.Username, c) + device := colorText(api.Msg.Sender.Username, messageSenderDeviceColor, c) + msgId := colorText(fmt.Sprintf("%d", api.Msg.ID), messageIdColor, c) + ts := colorText(fmt.Sprintf("%s", tm.Format(timeFormat)), messageTimeColor, c) + ret = strings.Replace(ret, "$MSG", msg, 1) + ret = strings.Replace(ret, "$USER", user, 1) + ret = strings.Replace(ret, "$DEVICE", device, 1) + ret = strings.Replace(ret, "$ID", msgId, 1) + ret = strings.Replace(ret, "$TIME", ts, 1) + ret = strings.Replace(ret, "```", fmt.Sprintf("\n\n"), -1) + } return ret } @@ -173,9 +179,9 @@ func populateList() { } else { clearView("List") - var recentPMs = "---[PMs]---\n" + var recentPMs = fmt.Sprintf("%s---[PMs]---%s\n", channelsHeaderColor, channelsColor); var recentPMsCount = 0 - var recentChannels = "---[Teams]---\n" + var recentChannels = fmt.Sprintf("%s---[Teams]---%s\n", channelsHeaderColor, channelsColor); var recentChannelsCount = 0 for _, s := range testVar.Result.Conversations { channels = append(channels, s.Channel) @@ -183,22 +189,22 @@ func populateList() { recentChannelsCount++ if recentChannelsCount <= ((maxY - 2) / 3) { if s.Unread { - recentChannels += "*" + recentChannels += fmt.Sprintf("%s*",color(0)) } - recentChannels += fmt.Sprintf("%s\n\t#%s\n", s.Channel.Name, s.Channel.TopicName) + recentChannels += fmt.Sprintf("%s\n\t#%s\n%s", s.Channel.Name, s.Channel.TopicName, channelsColor) } } else { recentPMsCount++ if recentPMsCount <= ((maxY - 2) / 3) { if s.Unread { - recentPMs += "*" + recentChannels += fmt.Sprintf("%s*",color(0)) } - recentPMs += fmt.Sprintf("%s\n", cleanChannelName(s.Channel.Name)) + recentPMs += fmt.Sprintf("%s\n%s", cleanChannelName(s.Channel.Name), channelsColor) } } } time.Sleep(1 * time.Millisecond) - printToView("List", fmt.Sprintf("%s%s", recentPMs, recentChannels)) + printToView("List", fmt.Sprintf("%s%s%s%s", channelsColor, recentPMs, recentChannels, noColor)) } } @@ -290,7 +296,7 @@ func handleTab() error { return err } else { // if you successfully get an input string, grab the last word from the string - ss := strings.Split(inputString, " ") + ss := regexp.MustCompile(`[ #]`).Split(inputString, -1) s := ss[len(ss)-1] // now in case the word (s) is a mention @something, lets remove it to normalize if strings.HasPrefix(s, "@") { @@ -385,7 +391,7 @@ func layout(g *gocui.Gui) error { } inputView.Editable = true inputView.Wrap = true - inputView.Title = " Not in a chat /j to join" + inputView.Title = fmt.Sprintf(" Not in a chat - write `%sj` to join", cmdPrefix) g.Cursor = true } if listView, err4 := g.SetView("List", 0, 0, maxX/2-maxX/3-1, maxY-1, 0); err4 != nil { @@ -530,6 +536,17 @@ func handleMessage(api keybase.ChatAPI) { } } +// It seems that golang doesn't have filter and other high order functions :'( +func delete_empty(s []string) []string { + var r []string + for _, str := range s { + if str != "" { + r = append(r, str) + } + } + return r +} + func handleInput(viewName string) error { clearView(viewName) inputString, _ := getInputString(viewName) @@ -537,7 +554,7 @@ func handleInput(viewName string) error { return nil } if strings.HasPrefix(inputString, cmdPrefix) { - cmd := strings.Split(inputString[len(cmdPrefix):], " ") + cmd := delete_empty(strings.Split(inputString[len(cmdPrefix):], " ")) if c, ok := commands[cmd[0]]; ok { c.Exec(cmd) return nil diff --git a/userConfigs.go b/userConfigs.go index 85dd744..d27ef61 100644 --- a/userConfigs.go +++ b/userConfigs.go @@ -1,8 +1,21 @@ package main - // Path where Downloaded files will default to var downloadPath = "/tmp/" +var colorless = false +var channelsColor = color(8) +var channelsHeaderColor = color(6) +var noColor = color(-1) +var mentionColor = color(3) +var messageHeaderColor = color(8) +var messageIdColor = color(7) +var messageTimeColor = color(6) +var messageSenderDefaultColor = color(8) +var messageSenderDeviceColor = color(8) +var messageBodyColor = noColor +var messageAttachmentColor = color(2) +var messageLinkColor = color(4) + // BASH-like PS1 variable equivalent (without colours) var outputFormat = "┌──[$USER@$DEVICE] [$ID] [$DATE - $TIME]\n└╼ $MSG"