summaryrefslogtreecommitdiff
path: root/chat/rooms.go
blob: 849aeb839a8e62c62222213e958c4731404f8071 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package chat

import (
	"errors"
)

var (
	NickAlreadyInUse = errors.New("Nickname is already in use")
	RoomIsFull       = errors.New("Room is full")
	EmptyNick        = errors.New("Nickname must not be empty")
	NickTooLong      = errors.New("NIckname is too long")
)

const (
	nickLenLimit = 16
	msgLenLimit  = 2000
)

// Room represents a chatroom.
type Room struct {
	messages chan Message
	buddies  map[string]*Buddy
	name     string
}

func newRoom(name string) (r *Room) {
	r = new(Room)
	r.messages = make(chan Message)
	r.buddies = make(map[string]*Buddy)
	r.name = name
	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)
		delete(rooms, r.name)
	} else {
		r.messages <- Message{
			Type: MsgLeave,
			User: nick,
		}
	}
}

func (r *Room) broadcast() {
	for m := range r.messages {
		if len(m.Text) > msgLenLimit {
			text := []rune(m.Text)
			text = text[:msgLenLimit]
			m.Text = string(text) + " (message truncated. was too long)"
		}

		for _, buddy := range r.buddies {
			buddy.Push(m)
		}
	}
}

// ListBuddies returns a list of nicknames of the connected buddies.
func (r *Room) ListBuddies() (buddies []string) {
	for nick := range r.buddies {
		buddies = append(buddies, nick)
	}
	return
}

var (
	rooms   map[string]*Room
	perroom int
)

// InitRooms initializes the internal rooms variable. Use this, before calling Join.
func InitRooms(room_limit int) {
	rooms = make(map[string]*Room)
	perroom = room_limit
}

// Join joins a buddy to a room. The room will be created, if it doesn't exist.
func Join(room, nick string) (*Buddy, *Room, error) {
	r, ok := rooms[room]
	if !ok {
		r = newRoom(room)
		rooms[room] = r
	}

	if _, there := r.buddies[nick]; there {
		return nil, nil, NickAlreadyInUse
	}

	if nick == "" {
		return nil, nil, EmptyNick
	}

	if len(nick) > nickLenLimit {
		return nil, nil, NickTooLong
	}

	if len(r.buddies) >= perroom {
		return nil, nil, RoomIsFull
	}

	r.messages <- Message{
		Type: MsgJoin,
		User: nick,
	}

	b := newBuddy(nick, r)
	r.buddies[nick] = b
	return b, r, nil
}