summaryrefslogtreecommitdiff
path: root/chat
diff options
context:
space:
mode:
authorKevin Chabowski <kevin@kch42.de>2014-03-25 15:54:32 +0100
committerKevin Chabowski <kevin@kch42.de>2014-03-25 15:54:32 +0100
commit966c17bb84866f10fc9e995df4107ed11ed2bdce (patch)
tree5e0446c4bb80dcefd7e705ce8de38d1a06989418 /chat
parent3aa1c450a3376a4339354579f90b6111b963c78a (diff)
downloadsimplechat-966c17bb84866f10fc9e995df4107ed11ed2bdce.tar.gz
simplechat-966c17bb84866f10fc9e995df4107ed11ed2bdce.tar.bz2
simplechat-966c17bb84866f10fc9e995df4107ed11ed2bdce.zip
Move protocol agnostic stuff into own package.
So we can test it more easily.
Diffstat (limited to 'chat')
-rw-r--r--chat/buddy.go41
-rw-r--r--chat/messages.go33
-rw-r--r--chat/rooms.go84
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
+}