From ffe23fa91ff5325d261521d6f33a10433abd9658 Mon Sep 17 00:00:00 2001 From: "Andreas Renberg (IQAndreas)" Date: Sat, 13 Dec 2014 03:08:33 -0600 Subject: [PATCH 1/4] Use a greyish color for system messages This color is easily adjusted --- client.go | 58 ++++++++++++++++++++++++++++++++----------------------- server.go | 19 +++++++++++------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/client.go b/client.go index 33bd4f7..a44ef11 100644 --- a/client.go +++ b/client.go @@ -11,16 +11,18 @@ import ( const MSG_BUFFER int = 10 -const HELP_TEXT string = `-> Available commands: +const HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + ` +-> Available commands: /about /exit /help /list /nick $NAME /whois $NAME -` +` + RESET -const ABOUT_TEXT string = `-> ssh-chat is made by @shazow. +const ABOUT_TEXT string = SYSTEM_MESSAGE_FORMAT + ` +-> ssh-chat is made by @shazow. It is a custom ssh server built in Go to serve a chat experience instead of a shell. @@ -28,7 +30,7 @@ const ABOUT_TEXT string = `-> ssh-chat is made by @shazow. Source: https://github.com/shazow/ssh-chat For more, visit shazow.net or follow at twitter.com/shazow -` +` + RESET type Client struct { Server *Server @@ -59,6 +61,14 @@ func (c *Client) ColoredName() string { return ColorString(c.Color, c.Name) } +func (c *Client) SysMsg(msg string, args ...interface{}) { + c.Msg <- SYSTEM_MESSAGE_FORMAT + "-> " + fmt.Sprintf(msg, args...) + RESET +} + +func (c *Client) SysMsg2(msg string, args ...interface{}) { + c.Write(SYSTEM_MESSAGE_FORMAT + "-> " + fmt.Sprintf(msg, args...) + RESET) +} + func (c *Client) Write(msg string) { c.term.Write([]byte(msg + "\r\n")) } @@ -141,7 +151,7 @@ func (c *Client) handleShell(channel ssh.Channel) { } msg := fmt.Sprintf("** %s%s", c.ColoredName(), me) if c.IsSilenced() || len(msg) > 1000 { - c.Msg <- fmt.Sprintf("-> Message rejected.") + c.SysMsg("Message rejected.") } else { c.Server.Broadcast(msg, nil) } @@ -149,7 +159,7 @@ func (c *Client) handleShell(channel ssh.Channel) { if len(parts) == 2 { c.Server.Rename(c, parts[1]) } else { - c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME") + c.SysMsg("Missing $NAME from: /nick $NAME") } case "/whois": if len(parts) == 2 { @@ -159,28 +169,28 @@ func (c *Client) handleShell(channel ssh.Channel) { if len(version) > 100 { version = "Evil Jerk with a superlong string" } - c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.ColoredName(), client.Fingerprint(), version) + c.SysMsg("%s is %s via %s", client.ColoredName(), client.Fingerprint(), version) } else { - c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1]) + c.SysMsg("No such name: %s", parts[1]) } } else { - c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME") + c.SysMsg("Missing $NAME from: /whois $NAME") } case "/list": names := c.Server.List(nil) - c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", ")) + c.SysMsg("%d connected: %s", len(names), strings.Join(names, ", ")) case "/ban": if !c.Server.IsOp(c) { - c.Msg <- fmt.Sprintf("-> You're not an admin.") + c.SysMsg("You're not an admin.") } else if len(parts) != 2 { - c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME") + c.SysMsg("Missing $NAME from: /ban $NAME") } else { client := c.Server.Who(parts[1]) if client == nil { - c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1]) + c.SysMsg("No such name: %s", parts[1]) } else { fingerprint := client.Fingerprint() - client.Write(fmt.Sprintf("-> Banned by %s.", c.ColoredName())) + client.SysMsg2("Banned by %s.", c.ColoredName()) c.Server.Ban(fingerprint, nil) client.Conn.Close() c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil) @@ -188,24 +198,24 @@ func (c *Client) handleShell(channel ssh.Channel) { } case "/op": if !c.Server.IsOp(c) { - c.Msg <- fmt.Sprintf("-> You're not an admin.") + c.SysMsg("You're not an admin.") } else if len(parts) != 2 { - c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME") + c.SysMsg("Missing $NAME from: /op $NAME") } else { client := c.Server.Who(parts[1]) if client == nil { - c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1]) + c.SysMsg("No such name: %s", parts[1]) } else { fingerprint := client.Fingerprint() - client.Write(fmt.Sprintf("-> Made op by %s.", c.ColoredName())) + client.SysMsg2("Made op by %s.", c.ColoredName()) c.Server.Op(fingerprint) } } case "/silence": if !c.Server.IsOp(c) { - c.Msg <- fmt.Sprintf("-> You're not an admin.") + c.SysMsg("You're not an admin.") } else if len(parts) < 2 { - c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME") + c.SysMsg("Missing $NAME from: /silence $NAME") } else { duration := time.Duration(5) * time.Minute if len(parts) >= 3 { @@ -216,21 +226,21 @@ func (c *Client) handleShell(channel ssh.Channel) { } client := c.Server.Who(parts[1]) if client == nil { - c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1]) + c.SysMsg("No such name: %s", parts[1]) } else { client.Silence(duration) - client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.ColoredName())) + client.SysMsg2("Silenced for %s by %s.", duration, c.ColoredName()) } } default: - c.Msg <- fmt.Sprintf("-> Invalid command: %s", line) + c.SysMsg("Invalid command: %s", line) } continue } msg := fmt.Sprintf("%s: %s", c.ColoredName(), line) if c.IsSilenced() || len(msg) > 1000 { - c.Msg <- fmt.Sprintf("-> Message rejected.") + c.SysMsg("Message rejected.") continue } c.Server.Broadcast(msg, c) diff --git a/server.go b/server.go index abdd61e..e197c10 100644 --- a/server.go +++ b/server.go @@ -76,6 +76,11 @@ func (s *Server) Len() int { return len(s.clients) } +const SYSTEM_MESSAGE_FORMAT string = "\033[1;3;90m" +func (s *Server) SysMsg(msg string, args ...interface{}) { + s.Broadcast(SYSTEM_MESSAGE_FORMAT + " * " + fmt.Sprintf(msg, args...) + RESET, nil) +} + func (s *Server) Broadcast(msg string, except *Client) { logger.Debugf("Broadcast to %d: %s", s.Len(), msg) s.history.Add(msg) @@ -91,7 +96,7 @@ func (s *Server) Broadcast(msg string, except *Client) { func (s *Server) Add(client *Client) { go func() { client.WriteLines(s.history.Get(10)) - client.Write(fmt.Sprintf("-> Welcome to ssh-chat. Enter /help for more.")) + client.SysMsg2("Welcome to ssh-chat. Enter /help for more.") }() s.lock.Lock() @@ -99,15 +104,15 @@ func (s *Server) Add(client *Client) { newName, err := s.proposeName(client.Name) if err != nil { - client.Msg <- fmt.Sprintf("-> Your name '%s' is not available, renamed to '%s'. Use /nick to change it.", client.ColoredName(), ColorString(client.Color, newName)) + client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick to change it.", client.ColoredName(), ColorString(client.Color, newName)) } client.Rename(newName) s.clients[client.Name] = client num := len(s.clients) s.lock.Unlock() - - s.Broadcast(fmt.Sprintf("* %s joined. (Total connected: %d)", client.ColoredName(), num), client) + + s.Broadcast(fmt.Sprintf("%s * %s joined. (Total connected: %d)%s", SYSTEM_MESSAGE_FORMAT, client.ColoredName(), num, RESET), client) } func (s *Server) Remove(client *Client) { @@ -115,7 +120,7 @@ func (s *Server) Remove(client *Client) { delete(s.clients, client.Name) s.lock.Unlock() - s.Broadcast(fmt.Sprintf("* %s left.", client.ColoredName()), nil) + s.SysMsg("%s left.", client.ColoredName()) } func (s *Server) proposeName(name string) (string, error) { @@ -143,7 +148,7 @@ func (s *Server) Rename(client *Client, newName string) { newName, err := s.proposeName(newName) if err != nil { - client.Msg <- fmt.Sprintf("-> %s", err) + client.SysMsg("%s", err) s.lock.Unlock() return } @@ -155,7 +160,7 @@ func (s *Server) Rename(client *Client, newName string) { s.clients[client.Name] = client s.lock.Unlock() - s.Broadcast(fmt.Sprintf("* %s is now known as %s.", oldName, newName), nil) + s.SysMsg("%s is now known as %s.", oldName, newName) } func (s *Server) List(prefix *string) []string { From de3df4e1a4670a8a20d38bf406283bb899ce8dcf Mon Sep 17 00:00:00 2001 From: "Andreas Renberg (IQAndreas)" Date: Sat, 13 Dec 2014 03:12:53 -0600 Subject: [PATCH 2/4] Fix name coloring when users are renamed --- server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.go b/server.go index e197c10..d238deb 100644 --- a/server.go +++ b/server.go @@ -160,7 +160,7 @@ func (s *Server) Rename(client *Client, newName string) { s.clients[client.Name] = client s.lock.Unlock() - s.SysMsg("%s is now known as %s.", oldName, newName) + s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName)) } func (s *Server) List(prefix *string) []string { From 46d00b3e557b168d40c79333065e6b5593b564f5 Mon Sep 17 00:00:00 2001 From: "Andreas Renberg (IQAndreas)" Date: Sat, 13 Dec 2014 04:01:37 -0600 Subject: [PATCH 3/4] Ugly hack which fixes two colors in a row This is a horrible hack to "continue" the previous string color and format after a RESET has been encountered. This is not HTML where you can just do a to resume your previous formatting! --- client.go | 4 ++-- colors.go | 13 +++++++++++-- server.go | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index a44ef11..804db32 100644 --- a/client.go +++ b/client.go @@ -62,11 +62,11 @@ func (c *Client) ColoredName() string { } func (c *Client) SysMsg(msg string, args ...interface{}) { - c.Msg <- SYSTEM_MESSAGE_FORMAT + "-> " + fmt.Sprintf(msg, args...) + RESET + c.Msg <- ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> " + fmt.Sprintf(msg, args...)) } func (c *Client) SysMsg2(msg string, args ...interface{}) { - c.Write(SYSTEM_MESSAGE_FORMAT + "-> " + fmt.Sprintf(msg, args...) + RESET) + c.Write(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> " + fmt.Sprintf(msg, args...))) } func (c *Client) Write(msg string) { diff --git a/colors.go b/colors.go index 16e8ad9..d086444 100644 --- a/colors.go +++ b/colors.go @@ -1,6 +1,7 @@ package main import ( + "strings" "math/rand" "time" ) @@ -8,6 +9,7 @@ import ( const RESET string = "\033[0m" const BOLD string = "\033[1m" const DIM string = "\033[2m" +const ITALIC string = "\033[3m" const UNDERLINE string = "\033[4m" const BLINK string = "\033[5m" const INVERT string = "\033[7m" @@ -19,6 +21,13 @@ func RandomColor() string { return colors[rand.Intn(len(colors))] } -func ColorString(format string, msg string) string { - return BOLD + "\033[" + format + "m" + msg + RESET +func ColorString(color string, msg string) string { + return BOLD + "\033[" + color + "m" + msg + RESET +} + +// Horrible hack to "continue" the previous string color and format +// after a RESET has been encountered. +// This is not HTML where you can just do a to resume your previous formatting! +func ContinuousFormat(format string, str string) string { + return SYSTEM_MESSAGE_FORMAT + strings.Replace(str, RESET, format, -1) + RESET } diff --git a/server.go b/server.go index d238deb..4edfe4d 100644 --- a/server.go +++ b/server.go @@ -78,7 +78,7 @@ func (s *Server) Len() int { const SYSTEM_MESSAGE_FORMAT string = "\033[1;3;90m" func (s *Server) SysMsg(msg string, args ...interface{}) { - s.Broadcast(SYSTEM_MESSAGE_FORMAT + " * " + fmt.Sprintf(msg, args...) + RESET, nil) + s.Broadcast(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, " * " + fmt.Sprintf(msg, args...)), nil) } func (s *Server) Broadcast(msg string, except *Client) { @@ -112,7 +112,7 @@ func (s *Server) Add(client *Client) { num := len(s.clients) s.lock.Unlock() - s.Broadcast(fmt.Sprintf("%s * %s joined. (Total connected: %d)%s", SYSTEM_MESSAGE_FORMAT, client.ColoredName(), num, RESET), client) + s.Broadcast(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.ColoredName(), num)), client) } func (s *Server) Remove(client *Client) { From 6d8d533bed6935411ce700b52ee3a0b3fbd2f20f Mon Sep 17 00:00:00 2001 From: "Andreas Renberg (IQAndreas)" Date: Sat, 13 Dec 2014 06:38:52 -0600 Subject: [PATCH 4/4] Allow for 256 different colors for username colors I have heard this should be fairly well supported, so I'm hoping these colors will all show up nicely for everyone. --- client.go | 2 +- colors.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index 804db32..7e256ee 100644 --- a/client.go +++ b/client.go @@ -51,7 +51,7 @@ func NewClient(server *Server, conn *ssh.ServerConn) *Client { Server: server, Conn: conn, Name: conn.User(), - Color: RandomColor(), + Color: RandomColor256(), Msg: make(chan string, MSG_BUFFER), ready: make(chan struct{}, 1), } diff --git a/colors.go b/colors.go index d086444..c5e83df 100644 --- a/colors.go +++ b/colors.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" "math/rand" "time" @@ -16,6 +17,11 @@ const INVERT string = "\033[7m" var colors = []string { "31", "32", "33", "34", "35", "36", "37", "91", "92", "93", "94", "95", "96", "97" } +func RandomColor256() string { + rand.Seed(time.Now().UTC().UnixNano()) + return fmt.Sprintf("38;05;%d", rand.Intn(256)) +} + func RandomColor() string { rand.Seed(time.Now().UTC().UnixNano()) return colors[rand.Intn(len(colors))]