package prompt import ( "fmt" "io" "strings" "time" "code.severnaya.net/kordophone-mock/v2/model" "code.severnaya.net/kordophone-mock/v2/server" "github.com/chzyer/readline" "github.com/google/uuid" "github.com/rs/zerolog/log" ) type Prompt struct { rl *readline.Instance server *server.Server } func (p *Prompt) conversationForGUID(guid string) (*model.Conversation, error) { if guid == "*" { // This means any conversation: return the first one return &p.server.SortedConversations()[0], nil } return p.server.ConversationForGUID(guid) } 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.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.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 (p *Prompt) receiveMessage(guid string, text string) { conversation, err := p.conversationForGUID(guid) if err != nil { log.Err(err).Msgf("Error receiving message for conversation %s", guid) return } message := model.Message{ Guid: uuid.New().String(), Sender: &conversation.Participants[0], Text: text, Date: time.Now(), } p.server.ReceiveMessage(conversation, message) } func NewPrompt(server *server.Server) *Prompt { completer := readline.NewPrefixCompleter( readline.PcItem("ls"), readline.PcItem("mark", readline.PcItem("-r"), ), readline.PcItem("help"), readline.PcItem("recv"), readline.PcItem("exit"), ) rl, err := readline.NewEx(&readline.Config{ Prompt: "\033[31m»\033[0m ", HistoryFile: "/tmp/readline.tmp", InterruptPrompt: "^C", EOFPrompt: "exit", AutoComplete: completer, HistorySearchFold: true, }) if err != nil { panic(err) } return &Prompt{ rl: rl, server: server, } } func (p *Prompt) StartInteractive() error { for { line, err := p.rl.Readline() if err == readline.ErrInterrupt { if len(line) == 0 { break } else { continue } } else if err == io.EOF { break } line = strings.TrimSpace(line) switch { 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] ") continue } read := false if args[1] == "-r" { read = true args = args[1:] } p.markConversation(args[1], read) case strings.HasPrefix(line, "recv"): // Receive args := strings.Split(line, " ") if len(args) < 3 { log.Info().Msgf("Usage: recv ") continue } body := strings.Join(args[2:], " ") // strip quotes if strings.HasPrefix(body, "\"") && strings.HasSuffix(body, "\"") { body = body[1 : len(body)-1] } p.receiveMessage(args[1], body) case line == "version": // Version fmt.Printf("Server version: %s\n", p.server.Version()) case line == "help": // Help fmt.Println("Usage: [args]") fmt.Println("Where is specified, '*' can be provided as a wildcard.") fmt.Println() fmt.Println("Commands:") fmt.Println("\tls list conversations") fmt.Println("\tls list messages for conversation") fmt.Println("\tmark [-r] mark conversation as unread/[r]ead") fmt.Println("\trecv receive a message") fmt.Println("\tversion print server version") fmt.Println("\thelp show this help") fmt.Println("\texit exits the program") case line == "exit": // Exit return nil default: fmt.Printf("Unknown command: %s\n", line) } } return nil } func (p *Prompt) CleanAndRefreshForLogging() { p.rl.Clean() // xxx: Lazy hack to make sure this runs _after_ the log is written. go p.rl.Refresh() }