ssh-chat/chat/{message,user,theme,history} -> ssh-chat/chat/message
authorAndrey Petrov <andrey.petrov@shazow.net>
Tue, 20 Jan 2015 23:57:01 +0000 (15:57 -0800)
committerAndrey Petrov <andrey.petrov@shazow.net>
Wed, 21 Jan 2015 00:33:52 +0000 (16:33 -0800)
21 files changed:
chat/command.go
chat/help.go
chat/message/history.go [moved from chat/history.go with 98% similarity]
chat/message/history_test.go [moved from chat/history_test.go with 98% similarity]
chat/message/identity.go [new file with mode: 0644]
chat/message/logger.go [new file with mode: 0644]
chat/message/message.go [moved from chat/message.go with 98% similarity]
chat/message/message_test.go [moved from chat/message_test.go with 81% similarity]
chat/message/screen_test.go [moved from chat/screen_test.go with 98% similarity]
chat/message/theme.go [moved from chat/theme.go with 99% similarity]
chat/message/theme_test.go [moved from chat/theme_test.go with 98% similarity]
chat/message/user.go [moved from chat/user.go with 93% similarity]
chat/message/user_test.go [moved from chat/user_test.go with 96% similarity]
chat/room.go
chat/room_test.go
chat/set.go
chat/set_test.go
cmd.go
host.go
host_test.go
identity.go

index eb085c93766d23274dafbb1637a15315a88e5477..76e4b219c10adc86aad84bef9c01d0c909040811 100644 (file)
@@ -6,6 +6,8 @@ import (
        "errors"
        "fmt"
        "strings"
+
+       "github.com/shazow/ssh-chat/chat/message"
 )
 
 // The error returned when an invalid command is issued.
@@ -29,7 +31,7 @@ type Command struct {
        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
 }
@@ -59,7 +61,7 @@ func (c Commands) Alias(command string, alias string) error {
 }
 
 // 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
        }
@@ -84,9 +86,9 @@ func (c Commands) Help(showOp bool) string {
                        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
 }
@@ -102,24 +104,24 @@ func init() {
 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
                },
        })
@@ -127,7 +129,7 @@ func InitCommands(c *Commands) {
        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
                },
@@ -138,7 +140,7 @@ func InitCommands(c *Commands) {
                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
@@ -164,11 +166,11 @@ func InitCommands(c *Commands) {
        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
                },
        })
@@ -178,7 +180,7 @@ func InitCommands(c *Commands) {
                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 {
@@ -187,16 +189,16 @@ func InitCommands(c *Commands) {
                                        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
                                }
                        }
@@ -207,7 +209,7 @@ func InitCommands(c *Commands) {
        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()
 
@@ -217,7 +219,7 @@ func InitCommands(c *Commands) {
                        } else {
                                body = "Quiet mode is toggled OFF"
                        }
-                       room.Send(NewSystemMsg(body, u))
+                       room.Send(message.NewSystemMsg(body, u))
                        return nil
                },
        })
@@ -225,7 +227,7 @@ func InitCommands(c *Commands) {
        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 {
@@ -234,7 +236,7 @@ func InitCommands(c *Commands) {
                                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
                },
        })
index 0ab62c6cf2c78b6393c3312992510dd4272f87d0..33fda2112a073801b30afa3fc21888c7143c8056 100644 (file)
@@ -4,6 +4,8 @@ import (
        "fmt"
        "sort"
        "strings"
+
+       "github.com/shazow/ssh-chat/chat/message"
 )
 
 type helpItem struct {
@@ -54,5 +56,5 @@ func (h help) String() string {
        }
 
        sort.Strings(r)
-       return strings.Join(r, Newline)
+       return strings.Join(r, message.Newline)
 }
similarity index 98%
rename from chat/history.go
rename to chat/message/history.go
index 6b999ca134fe97af1f1d8f0627ec60211d18cd63..6acc84cdb1e071ff2180e6bf708b1093c4a35f90 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "fmt"
similarity index 98%
rename from chat/history_test.go
rename to chat/message/history_test.go
index de767ec9bf9febd8983941183bb59b5592602667..4742bb12ef733cc0a9307465f9e2dd2a6ce820d9 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import "testing"
 
diff --git a/chat/message/identity.go b/chat/message/identity.go
new file mode 100644 (file)
index 0000000..4827b31
--- /dev/null
@@ -0,0 +1,26 @@
+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()
+}
diff --git a/chat/message/logger.go b/chat/message/logger.go
new file mode 100644 (file)
index 0000000..66e8ab4
--- /dev/null
@@ -0,0 +1,22 @@
+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{})
+}
similarity index 98%
rename from chat/message.go
rename to chat/message/message.go
index 9a0b30ae64fefa6f53385dc9c1a5aca2bb87121f..491e8b61cefce8196e88a51b4a527ec07b3530f1 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "fmt"
@@ -245,7 +245,6 @@ type CommandMsg struct {
        *PublicMsg
        command string
        args    []string
-       room    *Room
 }
 
 func (m *CommandMsg) Command() string {
@@ -255,3 +254,7 @@ func (m *CommandMsg) Command() string {
 func (m *CommandMsg) Args() []string {
        return m.args
 }
+
+func (m *CommandMsg) Body() string {
+       return m.body
+}
similarity index 81%
rename from chat/message_test.go
rename to chat/message/message_test.go
index bafe0149673bf4c3ffa5ebd53b00b83a39b52b95..1075ceeda54214f8fdba5c697bc20e51680816d7 100644 (file)
@@ -1,19 +1,7 @@
-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
 
@@ -23,7 +11,7 @@ func TestMessage(t *testing.T) {
                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 {
similarity index 98%
rename from chat/screen_test.go
rename to chat/message/screen_test.go
index c530f9429bb54d3a5d21f3436c988ad15bac29ff..3348adcecfff8db9470cfb780676863d08289d14 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "reflect"
similarity index 99%
rename from chat/theme.go
rename to chat/message/theme.go
index 27085c0f7f85599ceaed152b51cc497abaca4c81..8250b591564bba85ecf308c4ccad341fe61d8ff3 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import "fmt"
 
similarity index 98%
rename from chat/theme_test.go
rename to chat/message/theme_test.go
index 28b9d43136585ecd480985c752fbc89e53b41476..da0cd0a330075d249167595a8c05d02cd5bd8add 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "fmt"
similarity index 93%
rename from chat/user.go
rename to chat/message/user.go
index 4a31816462a41edcedab3c2c9a0e82425dee263d..73d384e7e5f66679e5c373699a62c8138846f1a9 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "errors"
@@ -15,13 +15,6 @@ const reHighlight = `\b(%s)\b`
 
 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
@@ -106,8 +99,8 @@ func (u *User) Consume(out io.Writer) {
 }
 
 // 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.
similarity index 96%
rename from chat/user_test.go
rename to chat/message/user_test.go
index 37ecc29828bacd3a587a5907656249597e486824..6e5caeb654f7a8e1e105ae1e6d76b5b3dfa13c10 100644 (file)
@@ -1,4 +1,4 @@
-package chat
+package message
 
 import (
        "reflect"
index 7d1b3af9592c1a7c2ae35574a59854922455c048..11f0bb1f8b92970d151964eba98a50179c5708ef 100644 (file)
@@ -5,6 +5,8 @@ import (
        "fmt"
        "io"
        "sync"
+
+       "github.com/shazow/ssh-chat/chat/message"
 )
 
 const historyLen = 20
@@ -20,16 +22,16 @@ var ErrInvalidName = errors.New("invalid name")
 
 // 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
@@ -37,11 +39,11 @@ type Room struct {
 
 // 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,
        }
@@ -56,7 +58,7 @@ func (r *Room) SetCommands(commands Commands) {
 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()
@@ -70,33 +72,33 @@ func (r *Room) SetLogging(out io.Writer) {
 }
 
 // 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
@@ -116,19 +118,19 @@ func (r *Room) Serve() {
 }
 
 // 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
        }
@@ -142,23 +144,23 @@ func (r *Room) Join(u *User) (*Member, error) {
        }
        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
        }
@@ -168,13 +170,13 @@ func (r *Room) Rename(oldId string, identity Identifier) error {
        }
 
        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
@@ -195,7 +197,7 @@ func (r *Room) MemberById(id string) (*Member, bool) {
 }
 
 // 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
 }
index e415a0c73277b3cfdd6665f0df90d59a7b89749a..05fbf02d62ef2c72d5fe3a4b12b62c52cc7659f7 100644 (file)
@@ -3,11 +3,29 @@ package chat
 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()
@@ -22,7 +40,7 @@ func TestRoomJoin(t *testing.T) {
        var expected, actual []byte
 
        s := &MockScreen{}
-       u := NewUser(testId("foo"))
+       u := message.NewUser(message.SimpleId("foo"))
 
        ch := NewRoom()
        go ch.Serve()
@@ -33,24 +51,24 @@ func TestRoomJoin(t *testing.T) {
                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)
@@ -58,8 +76,8 @@ func TestRoomJoin(t *testing.T) {
 }
 
 func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
-       u := NewUser(testId("foo"))
-       u.Config = UserConfig{
+       u := message.NewUser(message.SimpleId("foo"))
+       u.Config = message.UserConfig{
                Quiet: true,
        }
 
@@ -75,8 +93,8 @@ func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
        <-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)
                        }
                }
@@ -84,17 +102,17 @@ func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
 
        // 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,
        }
 
@@ -111,19 +129,19 @@ func TestRoomQuietToggleBroadcasts(t *testing.T) {
 
        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)
        }
 }
@@ -132,7 +150,7 @@ func TestQuietToggleDisplayState(t *testing.T) {
        var expected, actual []byte
 
        s := &MockScreen{}
-       u := NewUser(testId("foo"))
+       u := message.NewUser(message.SimpleId("foo"))
 
        ch := NewRoom()
        go ch.Serve()
@@ -146,17 +164,17 @@ func TestQuietToggleDisplayState(t *testing.T) {
        // 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) {
@@ -168,7 +186,7 @@ func TestRoomNames(t *testing.T) {
        var expected, actual []byte
 
        s := &MockScreen{}
-       u := NewUser(testId("foo"))
+       u := message.NewUser(message.SimpleId("foo"))
 
        ch := NewRoom()
        go ch.Serve()
@@ -182,9 +200,9 @@ func TestRoomNames(t *testing.T) {
        // 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)
index 8617e14484f74e8870d5fb39eef529cf79c4ab93..ed3c09f291b0d389a44385dd0e5bfb8d9f1009c9 100644 (file)
@@ -12,17 +12,22 @@ var ErrIdTaken = errors.New("id already taken")
 // 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{},
        }
 }
 
@@ -30,7 +35,7 @@ func NewSet() *Set {
 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
 }
@@ -41,7 +46,7 @@ func (s *Set) Len() int {
 }
 
 // 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()
@@ -49,7 +54,7 @@ func (s *Set) In(item Identifier) bool {
 }
 
 // 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()
@@ -62,7 +67,7 @@ func (s *Set) Get(id string) (Identifier, error) {
 }
 
 // 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()
 
@@ -76,7 +81,7 @@ func (s *Set) Add(item Identifier) error {
 }
 
 // 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()
@@ -88,9 +93,9 @@ func (s *Set) Remove(item Identifier) error {
        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()
 
@@ -107,7 +112,7 @@ func (s *Set) Replace(oldId string, item Identifier) error {
        }
        delete(s.lookup, oldId)
 
-       // Add new identifier
+       // Add new Item
        s.lookup[item.Id()] = item
 
        return nil
@@ -115,7 +120,7 @@ func (s *Set) Replace(oldId string, item Identifier) error {
 
 // 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)
@@ -124,8 +129,8 @@ func (s *Set) Each(fn func(item Identifier)) {
 }
 
 // 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()
index b92bdebe10d877acf9a24d7e4452201f5cec3d9f..c70ef2e298dbba2bf0fc3e7a62802fdd9fc4c7a6 100644 (file)
@@ -1,11 +1,15 @@
 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.")
@@ -20,7 +24,7 @@ func TestSet(t *testing.T) {
                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)
diff --git a/cmd.go b/cmd.go
index e60acb93cd51482cdd5963327b6e5177103e1ea4..55646fc08f30e9bdeb75e230466d01fbab77e22b 100644 (file)
--- a/cmd.go
+++ b/cmd.go
@@ -16,6 +16,7 @@ import (
        "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"
@@ -109,7 +110,7 @@ func main() {
 
        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)
diff --git a/host.go b/host.go
index baa50953173f08b560adb8cd39b30162c182e74c..aab7843c5475ef34015ac28c04549ccb57a27424 100644 (file)
--- a/host.go
+++ b/host.go
@@ -9,13 +9,14 @@ import (
 
        "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)
@@ -35,7 +36,7 @@ type Host struct {
        count int
 
        // Default theme
-       theme *chat.Theme
+       theme *message.Theme
 }
 
 // NewHost creates a Host on top of an existing listener.
@@ -72,7 +73,7 @@ func (h Host) isOp(conn sshd.Connection) bool {
 // 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.
@@ -83,7 +84,7 @@ func (h *Host) Connect(term *sshd.Terminal) {
 
        // Send MOTD
        if h.motd != "" {
-               user.Send(chat.NewAnnounceMsg(h.motd))
+               user.Send(message.NewAnnounceMsg(h.motd))
        }
 
        member, err := h.Join(user)
@@ -119,11 +120,11 @@ func (h *Host) Connect(term *sshd.Terminal) {
 
                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 == "" {
@@ -131,7 +132,7 @@ func (h *Host) Connect(term *sshd.Terminal) {
                        continue
                }
 
-               m := chat.ParseInput(line, user)
+               m := message.ParseInput(line, user)
 
                // FIXME: Any reason to use h.room.Send(m) instead?
                h.HandleMsg(m)
@@ -184,7 +185,7 @@ func (h Host) completeCommand(partial string) string {
 }
 
 // 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
@@ -231,8 +232,8 @@ func (h *Host) AutoCompleteFunction(u *chat.User) func(line string, pos int, key
        }
 }
 
-// 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
@@ -247,7 +248,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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:
@@ -261,7 +262,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                                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
                },
@@ -271,7 +272,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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:
@@ -283,7 +284,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                                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
                },
@@ -293,7 +294,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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")
@@ -305,7 +306,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                        }
 
                        id := target.Identifier.(*Identity)
-                       room.Send(chat.NewSystemMsg(id.Whois(), msg.From()))
+                       room.Send(message.NewSystemMsg(id.Whois(), msg.From()))
 
                        return nil
                },
@@ -314,8 +315,8 @@ func (h *Host) InitCommands(c *chat.Commands) {
        // 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
                },
        })
@@ -323,8 +324,8 @@ func (h *Host) InitCommands(c *chat.Commands) {
        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
                },
        })
@@ -335,7 +336,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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")
                        }
@@ -351,7 +352,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                        }
 
                        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
                },
@@ -362,7 +363,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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")
@@ -388,7 +389,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                        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())
@@ -402,7 +403,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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")
                        }
@@ -415,9 +416,9 @@ func (h *Host) InitCommands(c *chat.Commands) {
 
                        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
@@ -429,7 +430,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                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")
                        }
@@ -453,7 +454,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
                        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
                },
index 76bbe6bd9a84de9a3a352cfd170636730cdb4e04..0288ab2fe77c5abdb1ea5dd0e54e9d1b04355248 100644 (file)
@@ -10,7 +10,7 @@ import (
        "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"
 )
@@ -26,7 +26,7 @@ func stripPrompt(s string) string {
 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)
@@ -35,7 +35,7 @@ func TestHostGetPrompt(t *testing.T) {
                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 {
index bfd46fa07c466c35afdbb8e541d282bb3c085e96..4403d8b2edb79d7c4d0b42b1ff7c7dca14d0559e 100644 (file)
@@ -5,6 +5,7 @@ import (
        "net"
 
        "github.com/shazow/ssh-chat/chat"
+       "github.com/shazow/ssh-chat/chat/message"
        "github.com/shazow/ssh-chat/sshd"
 )
 
@@ -44,7 +45,7 @@ func (i Identity) Whois() string {
        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)
 }