package message import ( "fmt" "strings" "time" ) type Author interface { ID() string Name() string Color() int } // Message is an interface for messages. type Message interface { Render(*Theme) string String() string Command() string Timestamp() time.Time } type MessageTo interface { Message To() Author } type MessageFrom interface { Message From() Author } func ParseInput(body string, from Author) Message { m := NewPublicMsg(body, from) cmd, isCmd := m.ParseCommand() if isCmd { return cmd } return m } // Msg is a base type for other message types. type Msg struct { body string timestamp time.Time // TODO: themeCache *map[*Theme]string } func NewMsg(body string) *Msg { return &Msg{ body: body, timestamp: time.Now(), } } // Render message based on a theme. func (m Msg) Render(t *Theme) string { // TODO: Render based on theme // TODO: Cache based on theme return m.String() } func (m Msg) String() string { return m.body } func (m Msg) Command() string { return "" } func (m Msg) Timestamp() time.Time { return m.timestamp } // PublicMsg is any message from a user sent to the room. type PublicMsg struct { Msg from Author } func NewPublicMsg(body string, from Author) PublicMsg { return PublicMsg{ Msg: Msg{ body: body, timestamp: time.Now(), }, from: from, } } func (m PublicMsg) From() Author { return m.from } func (m PublicMsg) ParseCommand() (*CommandMsg, bool) { // Check if the message is a command if !strings.HasPrefix(m.body, "/") { return nil, false } // Parse // TODO: Handle quoted fields properly fields := strings.Fields(m.body) command, args := fields[0], fields[1:] msg := CommandMsg{ PublicMsg: m, command: command, args: args, } return &msg, true } func (m PublicMsg) Render(t *Theme) string { if t == nil { return m.String() } return fmt.Sprintf("%s: %s", t.ColorName(m.from), m.body) } func (m PublicMsg) RenderFor(cfg UserConfig) string { if cfg.Highlight == nil || cfg.Theme == nil { return m.Render(cfg.Theme) } if !cfg.Highlight.MatchString(m.body) { return m.Render(cfg.Theme) } body := cfg.Highlight.ReplaceAllString(m.body, cfg.Theme.Highlight("${1}")) if cfg.Bell { body += Bel } return fmt.Sprintf("%s: %s", cfg.Theme.ColorName(m.from), body) } func (m PublicMsg) String() string { return fmt.Sprintf("%s: %s", m.from.Name(), m.body) } // EmoteMsg is a /me message sent to the room. It specifically does not // extend PublicMsg because it doesn't implement MessageFrom to allow the // sender to see the emote. type EmoteMsg struct { Msg from Author } func NewEmoteMsg(body string, from Author) *EmoteMsg { return &EmoteMsg{ Msg: Msg{ body: body, timestamp: time.Now(), }, from: from, } } func (m EmoteMsg) Render(t *Theme) string { return fmt.Sprintf("** %s %s", m.from.Name(), m.body) } func (m EmoteMsg) String() string { return m.Render(nil) } // PrivateMsg is a message sent to another user, not shown to anyone else. type PrivateMsg struct { PublicMsg to Author } func NewPrivateMsg(body string, from Author, to Author) PrivateMsg { return PrivateMsg{ PublicMsg: NewPublicMsg(body, from), to: to, } } func (m PrivateMsg) To() Author { return m.to } func (m PrivateMsg) Render(t *Theme) string { s := fmt.Sprintf("[PM from %s] %s", m.from.Name(), m.body) if t == nil { return s } return t.ColorPM(s) } func (m PrivateMsg) String() string { return m.Render(nil) } // SystemMsg is a response sent from the server directly to a user, not shown // to anyone else. Usually in response to something, like /help. type SystemMsg struct { Msg to Author } func NewSystemMsg(body string, to Author) *SystemMsg { return &SystemMsg{ Msg: Msg{ body: body, timestamp: time.Now(), }, to: to, } } func (m *SystemMsg) Render(t *Theme) string { if t == nil { return m.String() } return t.ColorSys(m.String()) } func (m *SystemMsg) String() string { return fmt.Sprintf("-> %s", m.body) } func (m *SystemMsg) To() Author { return m.to } // AnnounceMsg is a message sent from the server to everyone, like a join or // leave event. type AnnounceMsg struct { Msg } func NewAnnounceMsg(body string) *AnnounceMsg { return &AnnounceMsg{ Msg: Msg{ body: body, timestamp: time.Now(), }, } } func (m AnnounceMsg) Render(t *Theme) string { if t == nil { return m.String() } return t.ColorSys(m.String()) } func (m AnnounceMsg) String() string { return fmt.Sprintf(" * %s", m.body) } type CommandMsg struct { PublicMsg command string args []string } func (m CommandMsg) Command() string { return m.command } func (m CommandMsg) Args() []string { return m.args } func (m CommandMsg) Body() string { return m.body }