12 "github.com/shazow/ssh-chat/set"
15 const messageBuffer = 5
16 const messageTimeout = 5 * time.Second
17 const reHighlight = `\b(%s)\b`
18 const timestampTimeout = 30 * time.Minute
19 const timestampLayout = "2006-01-02 15:04:05 UTC"
21 var ErrUserClosed = errors.New("user closed")
23 // User definition, implemented set Item interface and io.Writer
37 replyTo *User // Set when user gets a /msg, for replying.
38 lastMsg time.Time // When the last message was rendered
41 func NewUser(identity Identifier) *User {
44 config: DefaultUserConfig,
46 msg: make(chan Message, messageBuffer),
47 done: make(chan struct{}),
50 u.setColorIdx(rand.Int())
55 func NewUserScreen(identity Identifier, screen io.WriteCloser) *User {
56 u := NewUser(identity)
62 func (u *User) Joined() time.Time {
66 func (u *User) Config() UserConfig {
72 func (u *User) SetConfig(cfg UserConfig) {
78 // Rename the user with a new Identifier.
79 func (u *User) SetID(id string) {
80 u.Identifier.SetID(id)
81 u.setColorIdx(rand.Int())
84 // ReplyTo returns the last user that messaged this user.
85 func (u *User) ReplyTo() *User {
91 // SetReplyTo sets the last user to message this user.
92 func (u *User) SetReplyTo(user *User) {
98 // setColorIdx will set the colorIdx to a specific value, primarily used for
100 func (u *User) setColorIdx(idx int) {
104 // Disconnect user, stop accepting messages
105 func (u *User) Close() {
106 u.closeOnce.Do(func() {
110 // close(u.msg) TODO: Close?
115 // Consume message buffer into the handler. Will block, should be called in a
117 func (u *User) Consume() {
122 case m, ok := <-u.msg:
131 // Consume one message and stop, mostly for testing
132 func (u *User) ConsumeOne() Message {
136 // Check if there are pending messages, used for testing
137 func (u *User) HasMessages() bool {
147 // SetHighlight sets the highlighting regular expression to match string.
148 func (u *User) SetHighlight(s string) error {
149 re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
154 u.config.Highlight = re
159 func (u *User) render(m Message) string {
162 switch m := m.(type) {
165 out += m.RenderSelf(cfg)
167 out += m.RenderFor(cfg)
170 out += m.Render(cfg.Theme)
175 out += m.Render(cfg.Theme)
178 return cfg.Theme.Timestamp(m.Timestamp()) + " " + out + Newline
183 // writeMsg renders the message and attempts to write it, will Close the user
185 func (u *User) writeMsg(m Message) error {
187 _, err := u.screen.Write([]byte(r))
189 logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
195 // HandleMsg will render the message to the screen, blocking.
196 func (u *User) HandleMsg(m Message) error {
198 u.lastMsg = m.Timestamp()
203 // Add message to consume by user
204 func (u *User) Send(m Message) error {
209 case <-time.After(messageTimeout):
210 logger.Printf("Message buffer full, closing: %s", u.Name())
217 // Container for per-user configurations.
218 type UserConfig struct {
219 Highlight *regexp.Regexp
226 // Default user configuration to use
227 var DefaultUserConfig UserConfig
230 DefaultUserConfig = UserConfig{
236 // TODO: Seed random?