sshchat: Echo command messages with the new timestamp code.

This commit is contained in:
Andrey Petrov 2019-03-17 13:50:01 -04:00
parent 8282fad7dc
commit 9c3db55c83
3 changed files with 63 additions and 17 deletions

View File

@ -170,6 +170,8 @@ func (u *User) render(m Message) string {
if cfg.Bell { if cfg.Bell {
out += Bel out += Bel
} }
case *CommandMsg:
out += m.RenderSelf(cfg)
default: default:
out += m.Render(cfg.Theme) out += m.Render(cfg.Theme)
} }

23
host.go
View File

@ -162,6 +162,20 @@ func (h *Host) Connect(term *sshd.Terminal) {
m := message.ParseInput(line, user) m := message.ParseInput(line, user)
// Gross hack to override line echo in golang.org/x/crypto/ssh/terminal
// It needs to live before we render the resulting message.
term.Write([]byte{
27, '[', 'A', // Up
27, '[', '2', 'K', // Clear line
})
// May the gods have mercy on our souls.
if m, ok := m.(*message.CommandMsg); ok {
// Other messages render themselves by the room, commands we'll
// have to re-echo ourselves manually.
user.HandleMsg(m)
}
// FIXME: Any reason to use h.room.Send(m) instead? // FIXME: Any reason to use h.room.Send(m) instead?
h.HandleMsg(m) h.HandleMsg(m)
@ -175,15 +189,6 @@ func (h *Host) Connect(term *sshd.Terminal) {
term.SetPrompt(GetPrompt(user)) term.SetPrompt(GetPrompt(user))
user.SetHighlight(user.Name()) user.SetHighlight(user.Name())
} }
// Gross hack to override line echo in golang.org/x/crypto/ssh/terminal
term.Write([]byte{
27, // keyEscape
'[', 'A', // Up
27, // keyEscape
'[', '2', 'K', // Clear line
})
// May the gods have mercy on our souls.
} }
err = h.Leave(user) err = h.Leave(user)

View File

@ -6,6 +6,7 @@ import (
"crypto/rsa" "crypto/rsa"
"errors" "errors"
"io" "io"
mathRand "math/rand"
"strings" "strings"
"testing" "testing"
@ -15,22 +16,53 @@ import (
) )
func stripPrompt(s string) string { func stripPrompt(s string) string {
pos := strings.LastIndex(s, "\033[K") // FIXME: Is there a better way to do this?
if pos < 0 { if endPos := strings.Index(s, "\x1b[K "); endPos > 0 {
return s[endPos+3:]
}
if endPos := strings.Index(s, "\x1b[2K "); endPos > 0 {
return s[endPos+4:]
}
if endPos := strings.Index(s, "] "); endPos > 0 {
return s[endPos+2:]
}
return s return s
} }
return s[pos+3:]
func TestStripPrompt(t *testing.T) {
tests := []struct {
Input string
Want string
}{
{
Input: "\x1b[A\x1b[2K[quux] hello",
Want: "hello",
},
{
Input: "[foo] \x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[K * Guest1 joined. (Connected: 2)\r",
Want: " * Guest1 joined. (Connected: 2)\r",
},
}
for i, tc := range tests {
if got, want := stripPrompt(tc.Input), tc.Want; got != want {
t.Errorf("case #%d:\n got: %q\nwant: %q", i, got, want)
}
}
} }
func TestHostGetPrompt(t *testing.T) { func TestHostGetPrompt(t *testing.T) {
var expected, actual string var expected, actual string
// Make the random colors consistent across tests
mathRand.Seed(1)
u := message.NewUser(&Identity{id: "foo"}) u := message.NewUser(&Identity{id: "foo"})
actual = GetPrompt(u) actual = GetPrompt(u)
expected = "[foo] " expected = "[foo] "
if actual != expected { if actual != expected {
t.Errorf("Got: %q; Expected: %q", actual, expected) t.Errorf("Invalid host prompt:\n Got: %q;\nWant: %q", actual, expected)
} }
u.SetConfig(message.UserConfig{ u.SetConfig(message.UserConfig{
@ -39,7 +71,7 @@ func TestHostGetPrompt(t *testing.T) {
actual = GetPrompt(u) actual = GetPrompt(u)
expected = "[\033[38;05;88mfoo\033[0m] " expected = "[\033[38;05;88mfoo\033[0m] "
if actual != expected { if actual != expected {
t.Errorf("Got: %q; Expected: %q", actual, expected) t.Errorf("Invalid host prompt:\n Got: %q;\nWant: %q", actual, expected)
} }
} }
@ -205,18 +237,23 @@ func TestHostKick(t *testing.T) {
// Change nicks, make sure op sticks // Change nicks, make sure op sticks
w.Write([]byte("/nick quux\r\n")) w.Write([]byte("/nick quux\r\n"))
scanner.Scan() // Prompt scanner.Scan() // Prompt
scanner.Scan() // Prompt echo
scanner.Scan() // Nick change response scanner.Scan() // Nick change response
// Signal for the second client to connect
connected <- struct{}{}
// Block until second client is here // Block until second client is here
connected <- struct{}{} connected <- struct{}{}
scanner.Scan() // Connected message scanner.Scan() // Connected message
w.Write([]byte("/kick bar\r\n")) w.Write([]byte("/kick bar\r\n"))
scanner.Scan() // Prompt scanner.Scan() // Prompt
scanner.Scan() // Prompt echo
scanner.Scan() scanner.Scan() // Kick result
if actual, expected := stripPrompt(scanner.Text()), " * bar was kicked by quux.\r"; actual != expected { if actual, expected := stripPrompt(scanner.Text()), " * bar was kicked by quux.\r"; actual != expected {
t.Errorf("Got %q; expected %q", actual, expected) t.Errorf("Failed to detect kick:\n Got: %q;\nWant: %q", actual, expected)
} }
kicked <- struct{}{} kicked <- struct{}{}
@ -231,6 +268,8 @@ func TestHostKick(t *testing.T) {
}() }()
go func() { go func() {
<-connected
// Second client // Second client
err := sshd.ConnectShell(addr, "bar", func(r io.Reader, w io.WriteCloser) error { err := sshd.ConnectShell(addr, "bar", func(r io.Reader, w io.WriteCloser) error {
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)