9 "github.com/shazow/ssh-chat/chat/message"
10 "github.com/shazow/ssh-chat/set"
16 // The error returned when a message is sent to a room that is already
18 var ErrRoomClosed = errors.New("room closed")
20 // The error returned when a user attempts to join with an invalid name, such
22 var ErrInvalidName = errors.New("invalid name")
24 // Member is a User with per-Room metadata attached to it.
29 // Room definition, also a Set of User Items
32 history *message.History
33 broadcast chan message.Message
42 // NewRoom creates a new room.
43 func NewRoom() *Room {
44 broadcast := make(chan message.Message, roomBuffer)
48 history: message.NewHistory(historyLen),
49 commands: *defaultCommands,
56 // SetCommands sets the room's command handlers.
57 func (r *Room) SetCommands(commands Commands) {
61 // Close the room and all the users it contains.
62 func (r *Room) Close() {
63 r.closeOnce.Do(func() {
65 r.Members.Each(func(_ string, item set.Item) error {
66 item.Value().(*Member).Close()
74 // SetLogging sets logging output for the room's history
75 func (r *Room) SetLogging(out io.Writer) {
76 r.history.SetOutput(out)
79 // HandleMsg reacts to a message, will block until done.
80 func (r *Room) HandleMsg(m message.Message) {
81 switch m := m.(type) {
82 case *message.CommandMsg:
84 err := r.commands.Run(r, cmd)
86 m := message.NewSystemMsg(fmt.Sprintf("Err: %s", err), cmd.From())
89 case message.MessageTo:
93 fromMsg, skip := m.(message.MessageFrom)
94 var skipUser *message.User
96 skipUser = fromMsg.From()
100 r.Members.Each(func(_ string, item set.Item) (err error) {
101 user := item.Value().(*Member).User
103 if fromMsg != nil && user.Ignored.In(fromMsg.From().ID()) {
104 // Skip because ignored
108 if skip && skipUser == user {
112 if _, ok := m.(*message.AnnounceMsg); ok {
113 if user.Config().Quiet {
114 // Skip announcements
124 // Serve will consume the broadcast room and handle the messages, should be
125 // run in a goroutine.
126 func (r *Room) Serve() {
127 for m := range r.broadcast {
132 // Send message, buffered by a chan.
133 func (r *Room) Send(m message.Message) {
137 // History feeds the room's recent message history to the user's handler.
138 func (r *Room) History(u *message.User) {
139 for _, m := range r.history.Get(historyLen) {
144 // Join the room as a user, will announce.
145 func (r *Room) Join(u *message.User) (*Member, error) {
146 // TODO: Check if closed
148 return nil, ErrInvalidName
151 err := r.Members.Add(set.Itemize(u.ID(), member))
156 s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), r.Members.Len())
157 r.Send(message.NewAnnounceMsg(s))
161 // Leave the room as a user, will announce. Mostly used during setup.
162 func (r *Room) Leave(u message.Identifier) error {
163 err := r.Members.Remove(u.ID())
168 s := fmt.Sprintf("%s left.", u.Name())
169 r.Send(message.NewAnnounceMsg(s))
173 // Rename member with a new identity. This will not call rename on the member.
174 func (r *Room) Rename(oldID string, u message.Identifier) error {
176 return ErrInvalidName
178 err := r.Members.Replace(oldID, set.Itemize(u.ID(), u))
183 s := fmt.Sprintf("%s is now known as %s.", oldID, u.ID())
184 r.Send(message.NewAnnounceMsg(s))
188 // Member returns a corresponding Member object to a User if the Member is
189 // present in this room.
190 func (r *Room) Member(u *message.User) (*Member, bool) {
191 m, ok := r.MemberByID(u.ID())
195 // Check that it's the same user
202 func (r *Room) MemberByID(id string) (*Member, bool) {
203 m, err := r.Members.Get(id)
207 return m.Value().(*Member), true
210 // IsOp returns whether a user is an operator in this room.
211 func (r *Room) IsOp(u *message.User) bool {
212 return r.Ops.In(u.ID())
215 // Topic of the room.
216 func (r *Room) Topic() string {
220 // SetTopic will set the topic of the room.
221 func (r *Room) SetTopic(s string) {
225 // NamesPrefix lists all members' names with a given prefix, used to query
226 // for autocompletion purposes.
227 func (r *Room) NamesPrefix(prefix string) []string {
228 items := r.Members.ListPrefix(prefix)
229 names := make([]string, len(items))
230 for i, item := range items {
231 names[i] = item.Value().(*Member).User.Name()