mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-04-18 17:57:41 +03:00
Merge remote-tracking branch 'origin/pr/6'
This commit is contained in:
commit
9f005ad18d
63
client.go
63
client.go
@ -11,8 +11,7 @@ import (
|
|||||||
|
|
||||||
const MSG_BUFFER int = 10
|
const MSG_BUFFER int = 10
|
||||||
|
|
||||||
const HELP_TEXT string = `-> Available commands:
|
const HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available commands:
|
||||||
<<<<<<< HEAD
|
|
||||||
/about - About this chat
|
/about - About this chat
|
||||||
/exit - Exit the chat
|
/exit - Exit the chat
|
||||||
/help - Show this help text
|
/help - Show this help text
|
||||||
@ -21,14 +20,16 @@ const HELP_TEXT string = `-> Available commands:
|
|||||||
/nick $NAME - Rename yourself to a new name
|
/nick $NAME - Rename yourself to a new name
|
||||||
/whois $NAME - Display information about another connected user
|
/whois $NAME - Display information about another connected user
|
||||||
/msg $NAME $MESSAGE
|
/msg $NAME $MESSAGE
|
||||||
`
|
` + RESET
|
||||||
const OP_HELP_TEXT string = `-> Available operator commands:
|
|
||||||
|
const OP_HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available operator commands:
|
||||||
/ban $NAME - Banish a user from the chat
|
/ban $NAME - Banish a user from the chat
|
||||||
/op $NAME - Promote a user to server operator
|
/op $NAME - Promote a user to server operator
|
||||||
/silence $NAME - Revoke a user's ability to speak
|
/silence $NAME - Revoke a user's ability to speak
|
||||||
`
|
`
|
||||||
|
|
||||||
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
|
It is a custom ssh server built in Go to serve a chat experience
|
||||||
instead of a shell.
|
instead of a shell.
|
||||||
@ -36,7 +37,7 @@ const ABOUT_TEXT string = `-> ssh-chat is made by @shazow.
|
|||||||
Source: https://github.com/shazow/ssh-chat
|
Source: https://github.com/shazow/ssh-chat
|
||||||
|
|
||||||
For more, visit shazow.net or follow at twitter.com/shazow
|
For more, visit shazow.net or follow at twitter.com/shazow
|
||||||
`
|
` + RESET
|
||||||
|
|
||||||
const REQUIRED_WAIT time.Duration = time.Second / 2
|
const REQUIRED_WAIT time.Duration = time.Second / 2
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ func NewClient(server *Server, conn *ssh.ServerConn) *Client {
|
|||||||
Server: server,
|
Server: server,
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
Name: conn.User(),
|
Name: conn.User(),
|
||||||
Color: RandomColor(),
|
Color: RandomColor256(),
|
||||||
Msg: make(chan string, MSG_BUFFER),
|
Msg: make(chan string, MSG_BUFFER),
|
||||||
ready: make(chan struct{}, 1),
|
ready: make(chan struct{}, 1),
|
||||||
lastTX: time.Now(),
|
lastTX: time.Now(),
|
||||||
@ -71,6 +72,14 @@ func (c *Client) ColoredName() string {
|
|||||||
return ColorString(c.Color, c.Name)
|
return ColorString(c.Color, c.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) SysMsg(msg string, args ...interface{}) {
|
||||||
|
c.Msg <- ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> "+fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SysMsg2(msg string, args ...interface{}) {
|
||||||
|
c.Write(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> "+fmt.Sprintf(msg, args...)))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Write(msg string) {
|
func (c *Client) Write(msg string) {
|
||||||
c.term.Write([]byte(msg + "\r\n"))
|
c.term.Write([]byte(msg + "\r\n"))
|
||||||
}
|
}
|
||||||
@ -158,7 +167,7 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
}
|
}
|
||||||
msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
|
msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
|
||||||
if c.IsSilenced() || len(msg) > 1000 {
|
if c.IsSilenced() || len(msg) > 1000 {
|
||||||
c.Msg <- fmt.Sprintf("-> Message rejected.")
|
c.SysMsg("Message rejected.")
|
||||||
} else {
|
} else {
|
||||||
c.Server.Broadcast(msg, nil)
|
c.Server.Broadcast(msg, nil)
|
||||||
}
|
}
|
||||||
@ -166,7 +175,7 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
c.Server.Rename(c, parts[1])
|
c.Server.Rename(c, parts[1])
|
||||||
} else {
|
} else {
|
||||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME")
|
c.SysMsg("Missing $NAME from: /nick $NAME")
|
||||||
}
|
}
|
||||||
case "/whois":
|
case "/whois":
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
@ -176,28 +185,28 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
if len(version) > 100 {
|
if len(version) > 100 {
|
||||||
version = "Evil Jerk with a superlong string"
|
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 {
|
} else {
|
||||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
c.SysMsg("No such name: %s", parts[1])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
|
c.SysMsg("Missing $NAME from: /whois $NAME")
|
||||||
}
|
}
|
||||||
case "/list":
|
case "/list":
|
||||||
names := c.Server.List(nil)
|
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":
|
case "/ban":
|
||||||
if !c.Server.IsOp(c) {
|
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 {
|
} else if len(parts) != 2 {
|
||||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
|
c.SysMsg("Missing $NAME from: /ban $NAME")
|
||||||
} else {
|
} else {
|
||||||
client := c.Server.Who(parts[1])
|
client := c.Server.Who(parts[1])
|
||||||
if client == nil {
|
if client == nil {
|
||||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
c.SysMsg("No such name: %s", parts[1])
|
||||||
} else {
|
} else {
|
||||||
fingerprint := client.Fingerprint()
|
fingerprint := client.Fingerprint()
|
||||||
client.Write(fmt.Sprintf("-> Banned by %s.", c.ColoredName()))
|
client.SysMsg2("Banned by %s.", c.ColoredName())
|
||||||
c.Server.Ban(fingerprint, nil)
|
c.Server.Ban(fingerprint, nil)
|
||||||
client.Conn.Close()
|
client.Conn.Close()
|
||||||
c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
|
c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
|
||||||
@ -205,24 +214,24 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
}
|
}
|
||||||
case "/op":
|
case "/op":
|
||||||
if !c.Server.IsOp(c) {
|
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 {
|
} else if len(parts) != 2 {
|
||||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME")
|
c.SysMsg("Missing $NAME from: /op $NAME")
|
||||||
} else {
|
} else {
|
||||||
client := c.Server.Who(parts[1])
|
client := c.Server.Who(parts[1])
|
||||||
if client == nil {
|
if client == nil {
|
||||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
c.SysMsg("No such name: %s", parts[1])
|
||||||
} else {
|
} else {
|
||||||
fingerprint := client.Fingerprint()
|
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)
|
c.Server.Op(fingerprint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "/silence":
|
case "/silence":
|
||||||
if !c.Server.IsOp(c) {
|
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 {
|
} else if len(parts) < 2 {
|
||||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME")
|
c.SysMsg("Missing $NAME from: /silence $NAME")
|
||||||
} else {
|
} else {
|
||||||
duration := time.Duration(5) * time.Minute
|
duration := time.Duration(5) * time.Minute
|
||||||
if len(parts) >= 3 {
|
if len(parts) >= 3 {
|
||||||
@ -233,10 +242,10 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
}
|
}
|
||||||
client := c.Server.Who(parts[1])
|
client := c.Server.Who(parts[1])
|
||||||
if client == nil {
|
if client == nil {
|
||||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
c.SysMsg("No such name: %s", parts[1])
|
||||||
} else {
|
} else {
|
||||||
client.Silence(duration)
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "/msg": /* Send a PM */
|
case "/msg": /* Send a PM */
|
||||||
@ -254,7 +263,7 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
|
c.SysMsg("Invalid command: %s", line)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -266,7 +275,7 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.IsSilenced() || len(msg) > 1000 {
|
if c.IsSilenced() || len(msg) > 1000 {
|
||||||
c.Msg <- fmt.Sprintf("-> Message rejected.")
|
c.SysMsg("Message rejected.")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Server.Broadcast(msg, c)
|
c.Server.Broadcast(msg, c)
|
||||||
|
19
colors.go
19
colors.go
@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -8,17 +10,30 @@ import (
|
|||||||
const RESET string = "\033[0m"
|
const RESET string = "\033[0m"
|
||||||
const BOLD string = "\033[1m"
|
const BOLD string = "\033[1m"
|
||||||
const DIM string = "\033[2m"
|
const DIM string = "\033[2m"
|
||||||
|
const ITALIC string = "\033[3m"
|
||||||
const UNDERLINE string = "\033[4m"
|
const UNDERLINE string = "\033[4m"
|
||||||
const BLINK string = "\033[5m"
|
const BLINK string = "\033[5m"
|
||||||
const INVERT string = "\033[7m"
|
const INVERT string = "\033[7m"
|
||||||
|
|
||||||
var colors = []string { "31", "32", "33", "34", "35", "36", "37", "91", "92", "93", "94", "95", "96", "97" }
|
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 {
|
func RandomColor() string {
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
return colors[rand.Intn(len(colors))]
|
return colors[rand.Intn(len(colors))]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ColorString(format string, msg string) string {
|
func ColorString(color string, msg string) string {
|
||||||
return BOLD + "\033[" + format + "m" + msg + RESET
|
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 </style> to resume your previous formatting!
|
||||||
|
func ContinuousFormat(format string, str string) string {
|
||||||
|
return SYSTEM_MESSAGE_FORMAT + strings.Replace(str, RESET, format, -1) + RESET
|
||||||
}
|
}
|
||||||
|
19
server.go
19
server.go
@ -78,6 +78,11 @@ func (s *Server) Len() int {
|
|||||||
return len(s.clients)
|
return len(s.clients)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SYSTEM_MESSAGE_FORMAT string = "\033[1;3;90m"
|
||||||
|
func (s *Server) SysMsg(msg string, args ...interface{}) {
|
||||||
|
s.Broadcast(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, " * " + fmt.Sprintf(msg, args...)), nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) Broadcast(msg string, except *Client) {
|
func (s *Server) Broadcast(msg string, except *Client) {
|
||||||
logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
|
logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
|
||||||
s.history.Add(msg)
|
s.history.Add(msg)
|
||||||
@ -111,7 +116,7 @@ func (s *Server) Privmsg(nick, message string, sender *Client) error {
|
|||||||
func (s *Server) Add(client *Client) {
|
func (s *Server) Add(client *Client) {
|
||||||
go func() {
|
go func() {
|
||||||
client.WriteLines(s.history.Get(10))
|
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()
|
s.lock.Lock()
|
||||||
@ -119,15 +124,15 @@ func (s *Server) Add(client *Client) {
|
|||||||
|
|
||||||
newName, err := s.proposeName(client.Name)
|
newName, err := s.proposeName(client.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.Msg <- fmt.Sprintf("-> Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
|
client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Rename(newName)
|
client.Rename(newName)
|
||||||
s.clients[client.Name] = client
|
s.clients[client.Name] = client
|
||||||
num := len(s.clients)
|
num := len(s.clients)
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
|
|
||||||
s.Broadcast(fmt.Sprintf("* %s joined. (Total connected: %d)", client.ColoredName(), num), client)
|
s.Broadcast(ContinuousFormat(SYSTEM_MESSAGE_FORMAT, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.ColoredName(), num)), client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Remove(client *Client) {
|
func (s *Server) Remove(client *Client) {
|
||||||
@ -135,7 +140,7 @@ func (s *Server) Remove(client *Client) {
|
|||||||
delete(s.clients, client.Name)
|
delete(s.clients, client.Name)
|
||||||
s.lock.Unlock()
|
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) {
|
func (s *Server) proposeName(name string) (string, error) {
|
||||||
@ -163,7 +168,7 @@ func (s *Server) Rename(client *Client, newName string) {
|
|||||||
|
|
||||||
newName, err := s.proposeName(newName)
|
newName, err := s.proposeName(newName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.Msg <- fmt.Sprintf("-> %s", err)
|
client.SysMsg("%s", err)
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -175,7 +180,7 @@ func (s *Server) Rename(client *Client, newName string) {
|
|||||||
s.clients[client.Name] = client
|
s.clients[client.Name] = client
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
|
|
||||||
s.Broadcast(fmt.Sprintf("* %s is now known as %s.", oldName, newName), nil)
|
s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) List(prefix *string) []string {
|
func (s *Server) List(prefix *string) []string {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user