"errors"
"fmt"
"strings"
+
+ "github.com/shazow/ssh-chat/chat/message"
)
// The error returned when an invalid command is issued.
PrefixHelp string
// If omitted, command is hidden from /help
Help string
- Handler func(*Room, CommandMsg) error
+ Handler func(*Room, message.CommandMsg) error
// Command requires Op permissions
Op bool
}
}
// Run executes a command message.
-func (c Commands) Run(room *Room, msg CommandMsg) error {
+func (c Commands) Run(room *Room, msg message.CommandMsg) error {
if msg.From == nil {
return ErrNoOwner
}
normal = append(normal, cmd)
}
}
- help := "Available commands:" + Newline + NewCommandsHelp(normal).String()
+ help := "Available commands:" + message.Newline + NewCommandsHelp(normal).String()
if showOp {
- help += Newline + "-> Operator commands:" + Newline + NewCommandsHelp(op).String()
+ help += message.Newline + "-> Operator commands:" + message.Newline + NewCommandsHelp(op).String()
}
return help
}
func InitCommands(c *Commands) {
c.Add(Command{
Prefix: "/help",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
op := room.IsOp(msg.From())
- room.Send(NewSystemMsg(room.commands.Help(op), msg.From()))
+ room.Send(message.NewSystemMsg(room.commands.Help(op), msg.From()))
return nil
},
})
c.Add(Command{
Prefix: "/me",
- Handler: func(room *Room, msg CommandMsg) error {
- me := strings.TrimLeft(msg.body, "/me")
+ Handler: func(room *Room, msg message.CommandMsg) error {
+ me := strings.TrimLeft(msg.Body(), "/me")
if me == "" {
me = "is at a loss for words."
} else {
me = me[1:]
}
- room.Send(NewEmoteMsg(me, msg.From()))
+ room.Send(message.NewEmoteMsg(me, msg.From()))
return nil
},
})
c.Add(Command{
Prefix: "/exit",
Help: "Exit the chat.",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
msg.From().Close()
return nil
},
Prefix: "/nick",
PrefixHelp: "NAME",
Help: "Rename yourself.",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
args := msg.Args()
if len(args) != 1 {
return ErrMissingArg
c.Add(Command{
Prefix: "/names",
Help: "List users who are connected.",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
// TODO: colorize
names := room.NamesPrefix("")
body := fmt.Sprintf("%d connected: %s", len(names), strings.Join(names, ", "))
- room.Send(NewSystemMsg(body, msg.From()))
+ room.Send(message.NewSystemMsg(body, msg.From()))
return nil
},
})
Prefix: "/theme",
PrefixHelp: "[mono|colors]",
Help: "Set your color theme.",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
user := msg.From()
args := msg.Args()
if len(args) == 0 {
theme = user.Config.Theme.Id()
}
body := fmt.Sprintf("Current theme: %s", theme)
- room.Send(NewSystemMsg(body, user))
+ room.Send(message.NewSystemMsg(body, user))
return nil
}
id := args[0]
- for _, t := range Themes {
+ for _, t := range message.Themes {
if t.Id() == id {
user.Config.Theme = &t
body := fmt.Sprintf("Set theme: %s", id)
- room.Send(NewSystemMsg(body, user))
+ room.Send(message.NewSystemMsg(body, user))
return nil
}
}
c.Add(Command{
Prefix: "/quiet",
Help: "Silence room announcements.",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
u := msg.From()
u.ToggleQuietMode()
} else {
body = "Quiet mode is toggled OFF"
}
- room.Send(NewSystemMsg(body, u))
+ room.Send(message.NewSystemMsg(body, u))
return nil
},
})
c.Add(Command{
Prefix: "/slap",
PrefixHelp: "NAME",
- Handler: func(room *Room, msg CommandMsg) error {
+ Handler: func(room *Room, msg message.CommandMsg) error {
var me string
args := msg.Args()
if len(args) == 0 {
me = fmt.Sprintf("slaps %s around a bit with a large trout.", strings.Join(args, " "))
}
- room.Send(NewEmoteMsg(me, msg.From()))
+ room.Send(message.NewEmoteMsg(me, msg.From()))
return nil
},
})
"fmt"
"sort"
"strings"
+
+ "github.com/shazow/ssh-chat/chat/message"
)
type helpItem struct {
}
sort.Strings(r)
- return strings.Join(r, Newline)
+ return strings.Join(r, message.Newline)
}
-package chat
+package message
import (
"fmt"
-package chat
+package message
import "testing"
--- /dev/null
+package message
+
+// Identifier is an interface that can uniquely identify itself.
+type Identifier interface {
+ Id() string
+ SetId(string)
+ Name() string
+}
+
+// SimpleId is a simple Identifier implementation used for testing.
+type SimpleId string
+
+// Id returns the Id as a string.
+func (i SimpleId) Id() string {
+ return string(i)
+}
+
+// SetId is a no-op
+func (i SimpleId) SetId(s string) {
+ // no-op
+}
+
+// Name returns the Id
+func (i SimpleId) Name() string {
+ return i.Id()
+}
--- /dev/null
+package message
+
+import "io"
+import stdlog "log"
+
+var logger *stdlog.Logger
+
+func SetLogger(w io.Writer) {
+ flags := stdlog.Flags()
+ prefix := "[chat/message] "
+ logger = stdlog.New(w, prefix, flags)
+}
+
+type nullWriter struct{}
+
+func (nullWriter) Write(data []byte) (int, error) {
+ return len(data), nil
+}
+
+func init() {
+ SetLogger(nullWriter{})
+}
-package chat
+package message
import (
"fmt"
*PublicMsg
command string
args []string
- room *Room
}
func (m *CommandMsg) Command() string {
func (m *CommandMsg) Args() []string {
return m.args
}
+
+func (m *CommandMsg) Body() string {
+ return m.body
+}
-package chat
+package message
import "testing"
-type testId string
-
-func (i testId) Id() string {
- return string(i)
-}
-func (i testId) SetId(s string) {
- // no-op
-}
-func (i testId) Name() string {
- return i.Id()
-}
-
func TestMessage(t *testing.T) {
var expected, actual string
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
}
- u := NewUser(testId("foo"))
+ u := NewUser(SimpleId("foo"))
expected = "foo: hello"
actual = NewPublicMsg("hello", u).String()
if actual != expected {
-package chat
+package message
import (
"reflect"
-package chat
+package message
import "fmt"
-package chat
+package message
import (
"fmt"
-package chat
+package message
import (
"errors"
var ErrUserClosed = errors.New("user closed")
-// Identifier is an interface that can uniquely identify itself.
-type Identifier interface {
- Id() string
- SetId(string)
- Name() string
-}
-
// User definition, implemented set Item interface and io.Writer
type User struct {
Identifier
}
// Consume one message and stop, mostly for testing
-func (u *User) ConsumeOne(out io.Writer) {
- u.HandleMsg(<-u.msg, out)
+func (u *User) ConsumeChan() <-chan Message {
+ return u.msg
}
// SetHighlight sets the highlighting regular expression to match string.
-package chat
+package message
import (
"reflect"
"fmt"
"io"
"sync"
+
+ "github.com/shazow/ssh-chat/chat/message"
)
const historyLen = 20
// Member is a User with per-Room metadata attached to it.
type Member struct {
- *User
+ *message.User
Op bool
}
// Room definition, also a Set of User Items
type Room struct {
topic string
- history *History
+ history *message.History
members *Set
- broadcast chan Message
+ broadcast chan message.Message
commands Commands
closed bool
closeOnce sync.Once
// NewRoom creates a new room.
func NewRoom() *Room {
- broadcast := make(chan Message, roomBuffer)
+ broadcast := make(chan message.Message, roomBuffer)
return &Room{
broadcast: broadcast,
- history: NewHistory(historyLen),
+ history: message.NewHistory(historyLen),
members: NewSet(),
commands: *defaultCommands,
}
func (r *Room) Close() {
r.closeOnce.Do(func() {
r.closed = true
- r.members.Each(func(m Identifier) {
+ r.members.Each(func(m Item) {
m.(*Member).Close()
})
r.members.Clear()
}
// HandleMsg reacts to a message, will block until done.
-func (r *Room) HandleMsg(m Message) {
+func (r *Room) HandleMsg(m message.Message) {
switch m := m.(type) {
- case *CommandMsg:
+ case *message.CommandMsg:
cmd := *m
err := r.commands.Run(r, cmd)
if err != nil {
- m := NewSystemMsg(fmt.Sprintf("Err: %s", err), cmd.from)
+ m := message.NewSystemMsg(fmt.Sprintf("Err: %s", err), cmd.From())
go r.HandleMsg(m)
}
- case MessageTo:
+ case message.MessageTo:
user := m.To()
user.Send(m)
default:
- fromMsg, skip := m.(MessageFrom)
- var skipUser *User
+ fromMsg, skip := m.(message.MessageFrom)
+ var skipUser *message.User
if skip {
skipUser = fromMsg.From()
}
r.history.Add(m)
- r.members.Each(func(u Identifier) {
+ r.members.Each(func(u Item) {
user := u.(*Member).User
if skip && skipUser == user {
// Skip
return
}
- if _, ok := m.(*AnnounceMsg); ok {
+ if _, ok := m.(*message.AnnounceMsg); ok {
if user.Config.Quiet {
// Skip
return
}
// Send message, buffered by a chan.
-func (r *Room) Send(m Message) {
+func (r *Room) Send(m message.Message) {
r.broadcast <- m
}
// History feeds the room's recent message history to the user's handler.
-func (r *Room) History(u *User) {
+func (r *Room) History(u *message.User) {
for _, m := range r.history.Get(historyLen) {
u.Send(m)
}
}
// Join the room as a user, will announce.
-func (r *Room) Join(u *User) (*Member, error) {
+func (r *Room) Join(u *message.User) (*Member, error) {
if r.closed {
return nil, ErrRoomClosed
}
}
r.History(u)
s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), r.members.Len())
- r.Send(NewAnnounceMsg(s))
+ r.Send(message.NewAnnounceMsg(s))
return &member, nil
}
// Leave the room as a user, will announce. Mostly used during setup.
-func (r *Room) Leave(u *User) error {
+func (r *Room) Leave(u message.Identifier) error {
err := r.members.Remove(u)
if err != nil {
return err
}
s := fmt.Sprintf("%s left.", u.Name())
- r.Send(NewAnnounceMsg(s))
+ r.Send(message.NewAnnounceMsg(s))
return nil
}
// Rename member with a new identity. This will not call rename on the member.
-func (r *Room) Rename(oldId string, identity Identifier) error {
+func (r *Room) Rename(oldId string, identity message.Identifier) error {
if identity.Id() == "" {
return ErrInvalidName
}
}
s := fmt.Sprintf("%s is now known as %s.", oldId, identity.Id())
- r.Send(NewAnnounceMsg(s))
+ r.Send(message.NewAnnounceMsg(s))
return nil
}
// Member returns a corresponding Member object to a User if the Member is
// present in this room.
-func (r *Room) Member(u *User) (*Member, bool) {
+func (r *Room) Member(u *message.User) (*Member, bool) {
m, ok := r.MemberById(u.Id())
if !ok {
return nil, false
}
// IsOp returns whether a user is an operator in this room.
-func (r *Room) IsOp(u *User) bool {
+func (r *Room) IsOp(u *message.User) bool {
m, ok := r.Member(u)
return ok && m.Op
}
import (
"reflect"
"testing"
+
+ "github.com/shazow/ssh-chat/chat/message"
)
+// Used for testing
+type MockScreen struct {
+ buffer []byte
+}
+
+func (s *MockScreen) Write(data []byte) (n int, err error) {
+ s.buffer = append(s.buffer, data...)
+ return len(data), nil
+}
+
+func (s *MockScreen) Read(p *[]byte) (n int, err error) {
+ *p = s.buffer
+ s.buffer = []byte{}
+ return len(*p), nil
+}
+
func TestRoomServe(t *testing.T) {
ch := NewRoom()
- ch.Send(NewAnnounceMsg("hello"))
+ ch.Send(message.NewAnnounceMsg("hello"))
received := <-ch.broadcast
actual := received.String()
var expected, actual []byte
s := &MockScreen{}
- u := NewUser(testId("foo"))
+ u := message.NewUser(message.SimpleId("foo"))
ch := NewRoom()
go ch.Serve()
t.Fatal(err)
}
- u.ConsumeOne(s)
- expected = []byte(" * foo joined. (Connected: 1)" + Newline)
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
}
- ch.Send(NewSystemMsg("hello", u))
- u.ConsumeOne(s)
- expected = []byte("-> hello" + Newline)
+ ch.Send(message.NewSystemMsg("hello", u))
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte("-> hello" + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
}
- ch.Send(ParseInput("/me says hello.", u))
- u.ConsumeOne(s)
- expected = []byte("** foo says hello." + Newline)
+ ch.Send(message.ParseInput("/me says hello.", u))
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte("** foo says hello." + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
}
func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
- u := NewUser(testId("foo"))
- u.Config = UserConfig{
+ u := message.NewUser(message.SimpleId("foo"))
+ u.Config = message.UserConfig{
Quiet: true,
}
<-ch.broadcast
go func() {
- for msg := range u.msg {
- if _, ok := msg.(*AnnounceMsg); ok {
+ for msg := range u.ConsumeChan() {
+ if _, ok := msg.(*message.AnnounceMsg); ok {
t.Errorf("Got unexpected `%T`", msg)
}
}
// Call with an AnnounceMsg and all the other types
// and assert we received only non-announce messages
- ch.HandleMsg(NewAnnounceMsg("Ignored"))
+ ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
// Assert we still get all other types of messages
- ch.HandleMsg(NewEmoteMsg("hello", u))
- ch.HandleMsg(NewSystemMsg("hello", u))
- ch.HandleMsg(NewPrivateMsg("hello", u, u))
- ch.HandleMsg(NewPublicMsg("hello", u))
+ ch.HandleMsg(message.NewEmoteMsg("hello", u))
+ ch.HandleMsg(message.NewSystemMsg("hello", u))
+ ch.HandleMsg(message.NewPrivateMsg("hello", u, u))
+ ch.HandleMsg(message.NewPublicMsg("hello", u))
}
func TestRoomQuietToggleBroadcasts(t *testing.T) {
- u := NewUser(testId("foo"))
- u.Config = UserConfig{
+ u := message.NewUser(message.SimpleId("foo"))
+ u.Config = message.UserConfig{
Quiet: true,
}
u.ToggleQuietMode()
- expectedMsg := NewAnnounceMsg("Ignored")
+ expectedMsg := message.NewAnnounceMsg("Ignored")
ch.HandleMsg(expectedMsg)
- msg := <-u.msg
- if _, ok := msg.(*AnnounceMsg); !ok {
+ msg := <-u.ConsumeChan()
+ if _, ok := msg.(*message.AnnounceMsg); !ok {
t.Errorf("Got: `%T`; Expected: `%T`", msg, expectedMsg)
}
u.ToggleQuietMode()
- ch.HandleMsg(NewAnnounceMsg("Ignored"))
- ch.HandleMsg(NewSystemMsg("hello", u))
- msg = <-u.msg
- if _, ok := msg.(*AnnounceMsg); ok {
+ ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
+ ch.HandleMsg(message.NewSystemMsg("hello", u))
+ msg = <-u.ConsumeChan()
+ if _, ok := msg.(*message.AnnounceMsg); ok {
t.Errorf("Got unexpected `%T`", msg)
}
}
var expected, actual []byte
s := &MockScreen{}
- u := NewUser(testId("foo"))
+ u := message.NewUser(message.SimpleId("foo"))
ch := NewRoom()
go ch.Serve()
// Drain the initial Join message
<-ch.broadcast
- ch.Send(ParseInput("/quiet", u))
- u.ConsumeOne(s)
- expected = []byte("-> Quiet mode is toggled ON" + Newline)
+ ch.Send(message.ParseInput("/quiet", u))
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte("-> Quiet mode is toggled ON" + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
}
- ch.Send(ParseInput("/quiet", u))
- u.ConsumeOne(s)
- expected = []byte("-> Quiet mode is toggled OFF" + Newline)
+ ch.Send(message.ParseInput("/quiet", u))
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte("-> Quiet mode is toggled OFF" + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
var expected, actual []byte
s := &MockScreen{}
- u := NewUser(testId("foo"))
+ u := message.NewUser(message.SimpleId("foo"))
ch := NewRoom()
go ch.Serve()
// Drain the initial Join message
<-ch.broadcast
- ch.Send(ParseInput("/names", u))
- u.ConsumeOne(s)
- expected = []byte("-> 1 connected: foo" + Newline)
+ ch.Send(message.ParseInput("/names", u))
+ u.HandleMsg(<-u.ConsumeChan(), s)
+ expected = []byte("-> 1 connected: foo" + message.Newline)
s.Read(&actual)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got: `%s`; Expected: `%s`", actual, expected)
// The error returned when a requested item does not exist in the set.
var ErrItemMissing = errors.New("item does not exist")
+// Interface for an item storeable in the set
+type Item interface {
+ Id() string
+}
+
// Set with string lookup.
// TODO: Add trie for efficient prefix lookup?
type Set struct {
- lookup map[string]Identifier
+ lookup map[string]Item
sync.RWMutex
}
// NewSet creates a new set.
func NewSet() *Set {
return &Set{
- lookup: map[string]Identifier{},
+ lookup: map[string]Item{},
}
}
func (s *Set) Clear() int {
s.Lock()
n := len(s.lookup)
- s.lookup = map[string]Identifier{}
+ s.lookup = map[string]Item{}
s.Unlock()
return n
}
}
// In checks if an item exists in this set.
-func (s *Set) In(item Identifier) bool {
+func (s *Set) In(item Item) bool {
s.RLock()
_, ok := s.lookup[item.Id()]
s.RUnlock()
}
// Get returns an item with the given Id.
-func (s *Set) Get(id string) (Identifier, error) {
+func (s *Set) Get(id string) (Item, error) {
s.RLock()
item, ok := s.lookup[id]
s.RUnlock()
}
// Add item to this set if it does not exist already.
-func (s *Set) Add(item Identifier) error {
+func (s *Set) Add(item Item) error {
s.Lock()
defer s.Unlock()
}
// Remove item from this set.
-func (s *Set) Remove(item Identifier) error {
+func (s *Set) Remove(item Item) error {
s.Lock()
defer s.Unlock()
id := item.Id()
return nil
}
-// Replace item from old id with new Identifier.
-// Used for moving the same identifier to a new Id, such as a rename.
-func (s *Set) Replace(oldId string, item Identifier) error {
+// Replace item from old id with new Item.
+// Used for moving the same Item to a new Id, such as a rename.
+func (s *Set) Replace(oldId string, item Item) error {
s.Lock()
defer s.Unlock()
}
delete(s.lookup, oldId)
- // Add new identifier
+ // Add new Item
s.lookup[item.Id()] = item
return nil
// Each loops over every item while holding a read lock and applies fn to each
// element.
-func (s *Set) Each(fn func(item Identifier)) {
+func (s *Set) Each(fn func(item Item)) {
s.RLock()
for _, item := range s.lookup {
fn(item)
}
// ListPrefix returns a list of items with a prefix, case insensitive.
-func (s *Set) ListPrefix(prefix string) []Identifier {
- r := []Identifier{}
+func (s *Set) ListPrefix(prefix string) []Item {
+ r := []Item{}
prefix = strings.ToLower(prefix)
s.RLock()
package chat
-import "testing"
+import (
+ "testing"
+
+ "github.com/shazow/ssh-chat/chat/message"
+)
func TestSet(t *testing.T) {
var err error
s := NewSet()
- u := NewUser(testId("foo"))
+ u := message.NewUser(message.SimpleId("foo"))
if s.In(u) {
t.Errorf("Set should be empty.")
t.Errorf("Set should contain user.")
}
- u2 := NewUser(testId("bar"))
+ u2 := message.NewUser(message.SimpleId("bar"))
err = s.Add(u2)
if err != nil {
t.Error(err)
"golang.org/x/crypto/ssh"
"github.com/shazow/ssh-chat/chat"
+ "github.com/shazow/ssh-chat/chat/message"
"github.com/shazow/ssh-chat/sshd"
)
import _ "net/http/pprof"
host := NewHost(s)
host.auth = auth
- host.theme = &chat.Themes[0]
+ host.theme = &message.Themes[0]
err = fromFile(options.Admin, func(line []byte) error {
key, _, _, _, err := ssh.ParseAuthorizedKey(line)
"github.com/shazow/rateio"
"github.com/shazow/ssh-chat/chat"
+ "github.com/shazow/ssh-chat/chat/message"
"github.com/shazow/ssh-chat/sshd"
)
const maxInputLength int = 1024
// GetPrompt will render the terminal prompt string based on the user.
-func GetPrompt(user *chat.User) string {
+func GetPrompt(user *message.User) string {
name := user.Name()
if user.Config.Theme != nil {
name = user.Config.Theme.ColorName(user)
count int
// Default theme
- theme *chat.Theme
+ theme *message.Theme
}
// NewHost creates a Host on top of an existing listener.
// Connect a specific Terminal to this host and its room.
func (h *Host) Connect(term *sshd.Terminal) {
id := NewIdentity(term.Conn)
- user := chat.NewUserScreen(id, term)
+ user := message.NewUserScreen(id, term)
user.Config.Theme = h.theme
go func() {
// Close term once user is closed.
// Send MOTD
if h.motd != "" {
- user.Send(chat.NewAnnounceMsg(h.motd))
+ user.Send(message.NewAnnounceMsg(h.motd))
}
member, err := h.Join(user)
err = ratelimit.Count(1)
if err != nil {
- user.Send(chat.NewSystemMsg("Message rejected: Rate limiting is in effect.", user))
+ user.Send(message.NewSystemMsg("Message rejected: Rate limiting is in effect.", user))
continue
}
if len(line) > maxInputLength {
- user.Send(chat.NewSystemMsg("Message rejected: Input too long.", user))
+ user.Send(message.NewSystemMsg("Message rejected: Input too long.", user))
continue
}
if line == "" {
continue
}
- m := chat.ParseInput(line, user)
+ m := message.ParseInput(line, user)
// FIXME: Any reason to use h.room.Send(m) instead?
h.HandleMsg(m)
}
// AutoCompleteFunction returns a callback for terminal autocompletion
-func (h *Host) AutoCompleteFunction(u *chat.User) func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
+func (h *Host) AutoCompleteFunction(u *message.User) func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
return func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
if key != 9 {
return
}
}
-// GetUser returns a chat.User based on a name.
-func (h *Host) GetUser(name string) (*chat.User, bool) {
+// GetUser returns a message.User based on a name.
+func (h *Host) GetUser(name string) (*message.User, bool) {
m, ok := h.MemberById(name)
if !ok {
return nil, false
Prefix: "/msg",
PrefixHelp: "USER MESSAGE",
Help: "Send MESSAGE to USER.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
args := msg.Args()
switch len(args) {
case 0:
return errors.New("user not found")
}
- m := chat.NewPrivateMsg(strings.Join(args[1:], " "), msg.From(), target)
+ m := message.NewPrivateMsg(strings.Join(args[1:], " "), msg.From(), target)
room.Send(m)
return nil
},
Prefix: "/reply",
PrefixHelp: "MESSAGE",
Help: "Reply with MESSAGE to the previous private message.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
args := msg.Args()
switch len(args) {
case 0:
return errors.New("no message to reply to")
}
- m := chat.NewPrivateMsg(strings.Join(args, " "), msg.From(), target)
+ m := message.NewPrivateMsg(strings.Join(args, " "), msg.From(), target)
room.Send(m)
return nil
},
Prefix: "/whois",
PrefixHelp: "USER",
Help: "Information about USER.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
args := msg.Args()
if len(args) == 0 {
return errors.New("must specify user")
}
id := target.Identifier.(*Identity)
- room.Send(chat.NewSystemMsg(id.Whois(), msg.From()))
+ room.Send(message.NewSystemMsg(id.Whois(), msg.From()))
return nil
},
// Hidden commands
c.Add(chat.Command{
Prefix: "/version",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
- room.Send(chat.NewSystemMsg(buildCommit, msg.From()))
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
+ room.Send(message.NewSystemMsg(buildCommit, msg.From()))
return nil
},
})
timeStarted := time.Now()
c.Add(chat.Command{
Prefix: "/uptime",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
- room.Send(chat.NewSystemMsg(time.Now().Sub(timeStarted).String(), msg.From()))
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
+ room.Send(message.NewSystemMsg(time.Now().Sub(timeStarted).String(), msg.From()))
return nil
},
})
Prefix: "/kick",
PrefixHelp: "USER",
Help: "Kick USER from the server.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
if !room.IsOp(msg.From()) {
return errors.New("must be op")
}
}
body := fmt.Sprintf("%s was kicked by %s.", target.Name(), msg.From().Name())
- room.Send(chat.NewAnnounceMsg(body))
+ room.Send(message.NewAnnounceMsg(body))
target.Close()
return nil
},
Prefix: "/ban",
PrefixHelp: "USER [DURATION]",
Help: "Ban USER from the server.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
// TODO: Would be nice to specify what to ban. Key? Ip? etc.
if !room.IsOp(msg.From()) {
return errors.New("must be op")
h.auth.BanAddr(id.RemoteAddr(), until)
body := fmt.Sprintf("%s was banned by %s.", target.Name(), msg.From().Name())
- room.Send(chat.NewAnnounceMsg(body))
+ room.Send(message.NewAnnounceMsg(body))
target.Close()
logger.Debugf("Banned: \n-> %s", id.Whois())
Prefix: "/motd",
PrefixHelp: "MESSAGE",
Help: "Set the MESSAGE of the day.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
if !room.IsOp(msg.From()) {
return errors.New("must be op")
}
h.motd = motd
body := fmt.Sprintf("New message of the day set by %s:", msg.From().Name())
- room.Send(chat.NewAnnounceMsg(body))
+ room.Send(message.NewAnnounceMsg(body))
if motd != "" {
- room.Send(chat.NewAnnounceMsg(motd))
+ room.Send(message.NewAnnounceMsg(motd))
}
return nil
Prefix: "/op",
PrefixHelp: "USER [DURATION]",
Help: "Set USER as admin.",
- Handler: func(room *chat.Room, msg chat.CommandMsg) error {
+ Handler: func(room *chat.Room, msg message.CommandMsg) error {
if !room.IsOp(msg.From()) {
return errors.New("must be op")
}
h.auth.Op(id.PublicKey(), until)
body := fmt.Sprintf("Made op by %s.", msg.From().Name())
- room.Send(chat.NewSystemMsg(body, member.User))
+ room.Send(message.NewSystemMsg(body, member.User))
return nil
},
"testing"
"time"
- "github.com/shazow/ssh-chat/chat"
+ "github.com/shazow/ssh-chat/chat/message"
"github.com/shazow/ssh-chat/sshd"
"golang.org/x/crypto/ssh"
)
func TestHostGetPrompt(t *testing.T) {
var expected, actual string
- u := chat.NewUser(&Identity{nil, "foo"})
+ u := message.NewUser(&Identity{nil, "foo"})
u.SetColorIdx(2)
actual = GetPrompt(u)
t.Errorf("Got: %q; Expected: %q", actual, expected)
}
- u.Config.Theme = &chat.Themes[0]
+ u.Config.Theme = &message.Themes[0]
actual = GetPrompt(u)
expected = "[\033[38;05;2mfoo\033[0m] "
if actual != expected {
"net"
"github.com/shazow/ssh-chat/chat"
+ "github.com/shazow/ssh-chat/chat/message"
"github.com/shazow/ssh-chat/sshd"
)
if i.PublicKey() != nil {
fingerprint = sshd.Fingerprint(i.PublicKey())
}
- return fmt.Sprintf("name: %s"+chat.Newline+
- " > ip: %s"+chat.Newline+
+ return fmt.Sprintf("name: %s"+message.Newline+
+ " > ip: %s"+message.Newline+
" > fingerprint: %s", i.Name(), ip, fingerprint)
}