12 "github.com/shazow/ssh-chat/common"
15 const messageBuffer = 5
16 const messageTimeout = 5 * time.Second
17 const reHighlight = `\b(%s)\b`
19 var ErrUserClosed = errors.New("user closed")
21 // User definition, implemented set Item interface and io.Writer
35 replyTo *User // Set when user gets a /msg, for replying.
38 func NewUser(identity Identifier) *User {
41 Config: DefaultUserConfig,
43 msg: make(chan Message, messageBuffer),
44 done: make(chan struct{}),
45 Ignored: common.NewIdSet(),
47 u.SetColorIdx(rand.Int())
52 func NewUserScreen(identity Identifier, screen io.WriteCloser) *User {
53 u := NewUser(identity)
59 // Rename the user with a new Identifier.
60 func (u *User) SetId(id string) {
61 u.Identifier.SetId(id)
62 u.SetColorIdx(rand.Int())
65 // ReplyTo returns the last user that messaged this user.
66 func (u *User) ReplyTo() *User {
72 // SetReplyTo sets the last user to message this user.
73 func (u *User) SetReplyTo(user *User) {
79 // ToggleQuietMode will toggle whether or not quiet mode is enabled
80 func (u *User) ToggleQuietMode() {
81 u.Config.Quiet = !u.Config.Quiet
84 // SetColorIdx will set the colorIdx to a specific value, primarily used for
86 func (u *User) SetColorIdx(idx int) {
90 // Block until user is closed
91 func (u *User) Wait() {
95 // Disconnect user, stop accepting messages
96 func (u *User) Close() {
97 u.closeOnce.Do(func() {
101 // close(u.msg) TODO: Close?
106 // Consume message buffer into the handler. Will block, should be called in a
108 func (u *User) Consume() {
113 case m, ok := <-u.msg:
122 // Consume one message and stop, mostly for testing
123 func (u *User) ConsumeOne() Message {
127 // Check if there are pending messages, used for testing
128 func (u *User) HasMessages() bool {
138 // SetHighlight sets the highlighting regular expression to match string.
139 func (u *User) SetHighlight(s string) error {
140 re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
144 u.Config.Highlight = re
148 func (u *User) render(m Message) string {
149 switch m := m.(type) {
151 return m.RenderFor(u.Config) + Newline
153 u.SetReplyTo(m.From())
154 return m.Render(u.Config.Theme) + Newline
156 return m.Render(u.Config.Theme) + Newline
160 // HandleMsg will render the message to the screen, blocking.
161 func (u *User) HandleMsg(m Message) error {
163 _, err := u.screen.Write([]byte(r))
165 logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
171 // Add message to consume by user
172 func (u *User) Send(m Message) error {
177 case <-time.After(messageTimeout):
178 logger.Printf("Message buffer full, closing: %s", u.Name())
185 func (u *User) Ignore(identified common.Identified) error {
186 if identified == nil {
187 return errors.New("user is nil.")
190 if identified.Id() == u.Id() {
191 return errors.New("cannot ignore self.")
194 if u.Ignored.In(identified) {
195 return errors.New("user already ignored.")
198 u.Ignored.Add(identified)
202 func (u *User) Unignore(id string) error {
204 return errors.New("user is nil.")
207 identified, err := u.Ignored.Get(id)
212 return u.Ignored.Remove(identified)
215 // Container for per-user configurations.
216 type UserConfig struct {
217 Highlight *regexp.Regexp
223 // Default user configuration to use
224 var DefaultUserConfig UserConfig
227 DefaultUserConfig = UserConfig{
232 // TODO: Seed random?