diff options
Diffstat (limited to 'chat')
-rw-r--r-- | chat/buddy.go | 41 | ||||
-rw-r--r-- | chat/messages.go | 33 | ||||
-rw-r--r-- | chat/rooms.go | 84 |
3 files changed, 158 insertions, 0 deletions
diff --git a/chat/buddy.go b/chat/buddy.go new file mode 100644 index 0000000..c6f07ad --- /dev/null +++ b/chat/buddy.go @@ -0,0 +1,41 @@ +package chat + +import ( + "time" +) + +type Buddy struct { + Nick string + Receive chan Message + room *Room +} + +func NewBuddy(nick string, room *Room) *Buddy { + return &Buddy{ + Nick: nick, + Receive: make(chan Message), + room: room, + } +} + +func (b *Buddy) Leave() { + b.room.Leave(b.Nick) +} + +func (b *Buddy) Push(msg Message) { + go func() { + select { + case b.Receive <- msg: + case <-time.Tick(time.Millisecond * 100): + } + }() +} + +// Say sends a text as a chat message of this user to the connected room. +func (b *Buddy) Say(text string) { + b.room.Messages <- Message{ + Type: MsgChat, + User: b.Nick, + Text: text, + } +} diff --git a/chat/messages.go b/chat/messages.go new file mode 100644 index 0000000..793fc6c --- /dev/null +++ b/chat/messages.go @@ -0,0 +1,33 @@ +package chat + +import ( + "encoding/json" + "errors" +) + +type MsgType int + +const ( + MsgChat MsgType = iota // Default + MsgJoin + MsgLeave +) + +func (mt *MsgType) MarshalJSON() ([]byte, error) { + switch *mt { + case MsgChat: + return json.Marshal("chat") + case MsgJoin: + return json.Marshal("join") + case MsgLeave: + return json.Marshal("leave") + } + + return nil, errors.New("Unknown message type") +} + +type Message struct { + Type MsgType `json:"type"` + User string `json:"user"` + Text string `json:"text,omitempty"` +} diff --git a/chat/rooms.go b/chat/rooms.go new file mode 100644 index 0000000..e954e29 --- /dev/null +++ b/chat/rooms.go @@ -0,0 +1,84 @@ +package chat + +import ( + "errors" +) + +type Room struct { + Messages chan Message + Buddies map[string]*Buddy +} + +func NewRoom() (r *Room) { + r = new(Room) + r.Messages = make(chan Message) + r.Buddies = make(map[string]*Buddy) + go r.Broadcast() + return +} + +func (r *Room) Leave(nick string) { + if _, ok := r.Buddies[nick]; !ok { + return + } + + delete(r.Buddies, nick) + if len(r.Buddies) == 0 { + close(r.Messages) + } else { + r.Messages <- Message{ + Type: MsgLeave, + User: nick, + } + } +} + +func (r *Room) Broadcast() { + for m := range r.Messages { + for _, buddy := range r.Buddies { + buddy.Push(m) + } + } +} + +func (r *Room) ListBuddies() (buddies []string) { + for nick := range r.Buddies { + buddies = append(buddies, nick) + } + return +} + +var ( + rooms map[string]*Room + perroom int +) + +func InitRooms(room_limit int) { + rooms = make(map[string]*Room) + perroom = room_limit +} + +func Join(room, nick string) (*Buddy, *Room, error) { + r, ok := rooms[room] + if !ok { + r = NewRoom() + rooms[room] = r + } + + if _, there := r.Buddies[nick]; there { + return nil, nil, errors.New("Nickname is already in use") + } + + if len(r.Buddies) >= perroom { + return nil, nil, errors.New("Room is full") + } + + r.Messages <- Message{ + Type: MsgJoin, + User: nick, + } + + b := NewBuddy(nick, r) + r.Buddies[nick] = b + return b, r, nil +} |