prompt: adds ls, help, mark
This commit is contained in:
8
main.go
8
main.go
@@ -1,18 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"flag"
|
"flag"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"code.severnaya.net/kordophone-mock/v2/web"
|
|
||||||
"code.severnaya.net/kordophone-mock/v2/prompt"
|
"code.severnaya.net/kordophone-mock/v2/prompt"
|
||||||
|
"code.severnaya.net/kordophone-mock/v2/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoggingHook struct{
|
type LoggingHook struct {
|
||||||
prompt *prompt.Prompt
|
prompt *prompt.Prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ func main() {
|
|||||||
log.Info().Msgf("Listening on %s", addr)
|
log.Info().Msgf("Listening on %s", addr)
|
||||||
go httpServer.ListenAndServe()
|
go httpServer.ListenAndServe()
|
||||||
|
|
||||||
rl := prompt.NewPrompt()
|
rl := prompt.NewPrompt(&s.Server)
|
||||||
|
|
||||||
// Hook logging so we can refresh the prompt when something is logged.
|
// Hook logging so we can refresh the prompt when something is logged.
|
||||||
log.Logger = log.Logger.Hook(&LoggingHook{prompt: rl})
|
log.Logger = log.Logger.Hook(&LoggingHook{prompt: rl})
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
type Conversation struct {
|
type Conversation struct {
|
||||||
Date time.Time `json:"date"`
|
Date time.Time `json:"date"`
|
||||||
@@ -10,3 +15,22 @@ type Conversation struct {
|
|||||||
LastMessagePreview string `json:"lastMessagePreview"`
|
LastMessagePreview string `json:"lastMessagePreview"`
|
||||||
Guid string `json:"guid"`
|
Guid string `json:"guid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) GetDisplayName() string {
|
||||||
|
if c.DisplayName == nil {
|
||||||
|
return strings.Join(c.Participants, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return *c.DisplayName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conversation) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.Str("guid", c.Guid)
|
||||||
|
e.Time("date", c.Date)
|
||||||
|
e.Int("unreadCount", c.UnreadCount)
|
||||||
|
e.Str("lastMessagePreview", c.LastMessagePreview)
|
||||||
|
e.Strs("participants", c.Participants)
|
||||||
|
if c.DisplayName != nil {
|
||||||
|
e.Str("displayName", *c.DisplayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
105
prompt/prompt.go
105
prompt/prompt.go
@@ -1,22 +1,83 @@
|
|||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"code.severnaya.net/kordophone-mock/v2/server"
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Prompt struct {
|
type Prompt struct {
|
||||||
rl *readline.Instance
|
rl *readline.Instance
|
||||||
|
server *server.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrompt() *Prompt {
|
func (p *Prompt) listConversations() {
|
||||||
|
conversations := p.server.SortedConversations()
|
||||||
|
for _, c := range conversations {
|
||||||
|
fmt.Printf("%s %s \t %s ", c.Guid, c.GetDisplayName(), c.Date.Format("2006-01-02 15:04:05"))
|
||||||
|
if c.UnreadCount > 0 {
|
||||||
|
fmt.Printf("(%d unread)", c.UnreadCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Prompt) listMessages(guid string) {
|
||||||
|
conversation, err := p.server.ConversationForGUID(guid)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msgf("Error listing messages for conversation %s", guid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messages := p.server.MessagesForConversation(conversation)
|
||||||
|
for _, m := range messages {
|
||||||
|
var sender string
|
||||||
|
if m.Sender == nil {
|
||||||
|
sender = "(Me)"
|
||||||
|
} else {
|
||||||
|
sender = *m.Sender
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s %s From: %s\n", m.Guid, m.Date.Format("2006-01-02 15:04:05"), sender)
|
||||||
|
fmt.Printf("\t %s\n", m.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Prompt) markConversation(guid string, read bool) {
|
||||||
|
conversation, err := p.server.ConversationForGUID(guid)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msgf("Error marking conversation %s as read", guid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if read {
|
||||||
|
conversation.UnreadCount = 0
|
||||||
|
} else {
|
||||||
|
conversation.UnreadCount = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrompt(server *server.Server) *Prompt {
|
||||||
|
completer := readline.NewPrefixCompleter(
|
||||||
|
readline.PcItem("ls"),
|
||||||
|
readline.PcItem("mark",
|
||||||
|
readline.PcItem("-r"),
|
||||||
|
),
|
||||||
|
readline.PcItem("help"),
|
||||||
|
readline.PcItem("exit"),
|
||||||
|
)
|
||||||
|
|
||||||
rl, err := readline.NewEx(&readline.Config{
|
rl, err := readline.NewEx(&readline.Config{
|
||||||
Prompt: "\033[31m»\033[0m ",
|
Prompt: "\033[31m»\033[0m ",
|
||||||
HistoryFile: "/tmp/readline.tmp",
|
HistoryFile: "/tmp/readline.tmp",
|
||||||
InterruptPrompt: "^C",
|
InterruptPrompt: "^C",
|
||||||
EOFPrompt: "exit",
|
EOFPrompt: "exit",
|
||||||
|
AutoComplete: completer,
|
||||||
|
|
||||||
HistorySearchFold: true,
|
HistorySearchFold: true,
|
||||||
})
|
})
|
||||||
@@ -27,6 +88,7 @@ func NewPrompt() *Prompt {
|
|||||||
|
|
||||||
return &Prompt{
|
return &Prompt{
|
||||||
rl: rl,
|
rl: rl,
|
||||||
|
server: server,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,11 +105,45 @@ func (p *Prompt) StartInteractive() error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case line == "exit":
|
case strings.HasPrefix(line, "ls"): // List
|
||||||
|
args := strings.Split(line, " ")
|
||||||
|
|
||||||
|
if len(args) == 1 {
|
||||||
|
p.listConversations()
|
||||||
|
} else {
|
||||||
|
p.listMessages(args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
case strings.HasPrefix(line, "mark"): // Mark
|
||||||
|
args := strings.Split(line, " ")
|
||||||
|
if len(args) < 2 {
|
||||||
|
log.Info().Msgf("Usage: mark [-r] <guid>")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
read := false
|
||||||
|
if args[1] == "-r" {
|
||||||
|
read = true
|
||||||
|
args = args[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
p.markConversation(args[1], read)
|
||||||
|
|
||||||
|
case line == "help": // Help
|
||||||
|
fmt.Println("Commands:")
|
||||||
|
fmt.Println("\tls list conversations")
|
||||||
|
fmt.Println("\tls <guid> list messages for conversation")
|
||||||
|
fmt.Println("\tmark [-r] <guid> mark conversation as unread/[r]ead")
|
||||||
|
fmt.Println("\texit exits the program")
|
||||||
|
|
||||||
|
case line == "exit": // Exit
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Info().Msgf("Line: %s", line)
|
fmt.Printf("Unknown command: %s\n", line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,4 +156,3 @@ func (p *Prompt) CleanAndRefreshForLogging() {
|
|||||||
// xxx: Lazy hack to make sure this runs _after_ the log is written.
|
// xxx: Lazy hack to make sure this runs _after_ the log is written.
|
||||||
go p.rl.Refresh()
|
go p.rl.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,11 +54,21 @@ func (s *Server) Conversations() []model.Conversation {
|
|||||||
return s.conversations
|
return s.conversations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) SortedConversations() []model.Conversation {
|
||||||
|
conversations := s.Conversations()
|
||||||
|
sort.Slice(conversations, func(i, j int) bool {
|
||||||
|
return conversations[i].Date.After(conversations[j].Date)
|
||||||
|
})
|
||||||
|
|
||||||
|
return conversations
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) ConversationForGUID(guid string) (*model.Conversation, error) {
|
func (s *Server) ConversationForGUID(guid string) (*model.Conversation, error) {
|
||||||
var conversation *model.Conversation = nil
|
var conversation *model.Conversation = nil
|
||||||
for _, c := range s.conversations {
|
for i := range s.conversations {
|
||||||
|
c := &s.conversations[i]
|
||||||
if c.Guid == guid {
|
if c.Guid == guid {
|
||||||
conversation = &c
|
conversation = c
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +129,7 @@ func (s *Server) CheckBearerToken(token string) bool {
|
|||||||
return s.authenticateToken(token)
|
return s.authenticateToken(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) MessagesForConversation(conversation model.Conversation) []model.Message {
|
func (s *Server) MessagesForConversation(conversation *model.Conversation) []model.Message {
|
||||||
messages := s.messageStore[conversation.Guid]
|
messages := s.messageStore[conversation.Guid]
|
||||||
sort.Slice(messages, func(i int, j int) bool {
|
sort.Slice(messages, func(i int, j int) bool {
|
||||||
return messages[i].Date.Before(messages[j].Date)
|
return messages[i].Date.Before(messages[j].Date)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (m *MockHTTPServer) handleMessages(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
messages := m.Server.MessagesForConversation(*conversation)
|
messages := m.Server.MessagesForConversation(conversation)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(messages)
|
jsonData, err := json.Marshal(messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user