ssh-chat/chat/message/message.go
themester 229c2d793d Adapted my repo to shazow. Changes...
- Added new rank: Administators (or masters). This rank can add or delete operators, but cannot add or remove another administrators. Administrators rank is designed by public key on server startup.
- Added "vim mode". With this mode you can use commands as ':' instead of '/'. As you can see in the code, the Prefix parameter of Command structure was changed by a neutral parameter for future introductions of prefixes.
- New Help function. This function add help for admin rank.
- Added the following commands:
  - setnick: Administrators can change the nick of another user.
  - private: Users can stablish privates conversations. This conversations are permanent until they execute endprivate command.
  - endprivate: Finish private conversation.
  - welcome: Prints motd. Only admins can execute this.
  - whois: In whois only admins can see ip.
  - delop: Allow admins to delete an operator (or moderator as is mentioned in some parts of the code).
- Changed historyLen and roomBuffer vars.
- In cmd tool have been added fsnotify to update public key files (admin and moderator public key files). This is to prevent to restart the server everytime we want to add an administrator.
- Added new prompt mode. In this mode you can see in which room you are talking. As default is 'general'. If you start private conversation the room will be the name of the another user. This part has been introduced for future implementations.
- As I said before now exists private conversations (unlike msg command).
2018-01-12 20:04:39 +01:00

265 lines
4.6 KiB
Go

package message
import (
"fmt"
"strings"
"time"
)
// Message is an interface for messages.
type Message interface {
Render(*Theme) string
String() string
Command() string
Timestamp() time.Time
}
type MessageTo interface {
Message
To() *User
}
type MessageFrom interface {
Message
From() *User
}
func ParseInput(body string, from *User) 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 *User
}
func NewPublicMsg(body string, from *User) PublicMsg {
return PublicMsg{
Msg: Msg{
body: body,
timestamp: time.Now(),
},
from: from,
}
}
func (m PublicMsg) From() *User {
return m.from
}
func (m PublicMsg) ParseCommand() (*CommandMsg, bool) {
// Check if the message is a command
if !strings.HasPrefix(m.body, "/") && !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 *User
}
func NewEmoteMsg(body string, from *User) *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 *User
}
func NewPrivateMsg(body string, from *User, to *User) PrivateMsg {
return PrivateMsg{
PublicMsg: NewPublicMsg(body, from),
to: to,
}
}
func (m PrivateMsg) To() *User {
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 *User
}
func NewSystemMsg(body string, to *User) *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() *User {
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
}