diff --git a/client.go b/client.go index 7cced1d..1aa0370 100644 --- a/client.go +++ b/client.go @@ -12,6 +12,7 @@ import ( const MSG_BUFFER int = 10 const HELP_TEXT string = `-> Available commands: +<<<<<<< HEAD /about - About this chat /exit - Exit the chat /help - Show this help text @@ -19,6 +20,7 @@ const HELP_TEXT string = `-> Available commands: /me $ACTION - Show yourself doing an action /nick $NAME - Rename yourself to a new name /whois $NAME - Display information about another connected user + /msg $NAME $MESSAGE ` const OP_HELP_TEXT string = `-> Available operator commands: /ban $NAME - Banish a user from the chat @@ -36,6 +38,8 @@ const ABOUT_TEXT string = `-> ssh-chat is made by @shazow. For more, visit shazow.net or follow at twitter.com/shazow ` +const REQUIRED_WAIT time.Duration = time.Second / 2 + type Client struct { Server *Server Conn *ssh.ServerConn @@ -48,6 +52,7 @@ type Client struct { termWidth int termHeight int silencedUntil time.Time + lastTX time.Time } func NewClient(server *Server, conn *ssh.ServerConn) *Client { @@ -58,6 +63,7 @@ func NewClient(server *Server, conn *ssh.ServerConn) *Client { Color: RandomColor(), Msg: make(chan string, MSG_BUFFER), ready: make(chan struct{}, 1), + lastTX: time.Now(), } } @@ -233,6 +239,20 @@ func (c *Client) handleShell(channel ssh.Channel) { client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.ColoredName())) } } + case "/msg": /* Send a PM */ + /* Make sure we have a recipient and a message */ + if len(parts) < 2 { + c.Msg <- fmt.Sprintf("-> Missing $NAME from: /msg $NAME $MESSAGE") + break + } else if len(parts) < 3 { + c.Msg <- fmt.Sprintf("-> Missing $MESSAGE from: /msg $NAME $MESSAGE") + break + } + /* Ask the server to send the message */ + if err := c.Server.Privmsg(parts[1], parts[2], c); nil != err { + c.Msg <- fmt.Sprintf("Unable to send message to %v: %v", parts[1], err) + } + default: c.Msg <- fmt.Sprintf("-> Invalid command: %s", line) } @@ -240,11 +260,17 @@ func (c *Client) handleShell(channel ssh.Channel) { } msg := fmt.Sprintf("%s: %s", c.ColoredName(), line) - if c.IsSilenced() || len(msg) > 1000 || len(line) < 1 { + /* Rate limit */ + if time.Now().Sub(c.lastTX) < REQUIRED_WAIT { + c.Msg <- fmt.Sprintf("-> Rate limiting in effect.") + continue + } + if c.IsSilenced() || len(msg) > 1000 { c.Msg <- fmt.Sprintf("-> Message rejected.") continue } c.Server.Broadcast(msg, c) + c.lastTX = time.Now() } } diff --git a/server.go b/server.go index b053bd5..0c076e6 100644 --- a/server.go +++ b/server.go @@ -16,7 +16,7 @@ import ( const MAX_NAME_LENGTH = 32 const HISTORY_LEN = 20 -var RE_STRIP_TEXT = regexp.MustCompile("[^0-9A-Za-z_]") +var RE_STRIP_TEXT = regexp.MustCompile("[^0-9A-Za-z_.-]") type Clients map[string]*Client @@ -86,10 +86,28 @@ func (s *Server) Broadcast(msg string, except *Client) { if except != nil && client == except { continue } - client.Msg <- msg + /* Add an ascii BEL to ding clients when they're mentioned */ + if strings.Contains(msg, client.Name) { + client.Msg <- msg + "\007" + } else { + client.Msg <- msg + } } } +/* Send a message to a particular nick, if it exists */ +func (s *Server) Privmsg(nick, message string, sender *Client) error { + /* Get the recipient */ + target, ok := s.clients[nick] + if !ok { + return fmt.Errorf("no client with that nick") + } + /* Send the message */ + target.Msg <- fmt.Sprintf("\007[PM from %v] %v", sender.Name, message) + logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message) + return nil +} + func (s *Server) Add(client *Client) { go func() { client.WriteLines(s.history.Get(10))