mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-04-14 16:17:17 +03:00
broken: Use Member rather than message.User when possible; chat tests are broken.
This commit is contained in:
parent
9ecd2a6fa2
commit
33a76bb7f4
@ -5,6 +5,7 @@ package chat
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/shazow/ssh-chat/chat/message"
|
||||
@ -110,7 +111,7 @@ func InitCommands(c *Commands) {
|
||||
c.Add(Command{
|
||||
Prefix: "/help",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
op := room.IsOp(msg.From())
|
||||
op := room.IsOp(msg.From().(Member))
|
||||
room.Send(message.NewSystemMsg(room.commands.Help(op), msg.From()))
|
||||
return nil
|
||||
},
|
||||
@ -135,8 +136,7 @@ func InitCommands(c *Commands) {
|
||||
Prefix: "/exit",
|
||||
Help: "Exit the chat.",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
msg.From().Close()
|
||||
return nil
|
||||
return msg.From().(io.Closer).Close()
|
||||
},
|
||||
})
|
||||
c.Alias("/exit", "/quit")
|
||||
@ -150,7 +150,7 @@ func InitCommands(c *Commands) {
|
||||
if len(args) != 1 {
|
||||
return ErrMissingArg
|
||||
}
|
||||
member, ok := room.MemberByID(msg.From().ID())
|
||||
member, ok := room.MemberByID(msg.From().(Member).ID())
|
||||
if !ok {
|
||||
return ErrMissingMember
|
||||
}
|
||||
@ -184,7 +184,7 @@ func InitCommands(c *Commands) {
|
||||
PrefixHelp: "[colors|...]",
|
||||
Help: "Set your color theme. (More themes: solarized, mono, hacker)",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
user := msg.From()
|
||||
user := msg.From().(Member)
|
||||
args := msg.Args()
|
||||
cfg := user.Config()
|
||||
if len(args) == 0 {
|
||||
@ -215,7 +215,7 @@ func InitCommands(c *Commands) {
|
||||
Prefix: "/quiet",
|
||||
Help: "Silence room announcements.",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
u := msg.From()
|
||||
u := msg.From().(Member)
|
||||
cfg := u.Config()
|
||||
cfg.Quiet = !cfg.Quiet
|
||||
u.SetConfig(cfg)
|
||||
@ -253,7 +253,7 @@ func InitCommands(c *Commands) {
|
||||
PrefixHelp: "[USER]",
|
||||
Help: "Hide messages from USER, /unignore USER to stop hiding.",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
from, ok := room.Member(msg.From())
|
||||
from, ok := room.Member(msg.From().(Member))
|
||||
if !ok {
|
||||
return ErrMissingMember
|
||||
}
|
||||
@ -278,7 +278,7 @@ func InitCommands(c *Commands) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if id == msg.From().ID() {
|
||||
if id == msg.From().(Member).ID() {
|
||||
return errors.New("cannot ignore self")
|
||||
}
|
||||
target, ok := room.MemberByID(id)
|
||||
@ -302,7 +302,7 @@ func InitCommands(c *Commands) {
|
||||
Prefix: "/unignore",
|
||||
PrefixHelp: "USER",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
from, ok := room.Member(msg.From())
|
||||
from, ok := room.Member(msg.From().(Member))
|
||||
if !ok {
|
||||
return ErrMissingMember
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ type roomMember struct {
|
||||
}
|
||||
|
||||
type Member interface {
|
||||
ID() string
|
||||
message.Author
|
||||
|
||||
Name() string
|
||||
ID() string
|
||||
SetName(string)
|
||||
|
||||
Config() message.UserConfig
|
||||
|
@ -6,6 +6,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Author interface {
|
||||
Name() string
|
||||
Color() int
|
||||
}
|
||||
|
||||
// Message is an interface for messages.
|
||||
type Message interface {
|
||||
Render(*Theme) string
|
||||
@ -16,15 +21,15 @@ type Message interface {
|
||||
|
||||
type MessageTo interface {
|
||||
Message
|
||||
To() *User
|
||||
To() Author
|
||||
}
|
||||
|
||||
type MessageFrom interface {
|
||||
Message
|
||||
From() *User
|
||||
From() Author
|
||||
}
|
||||
|
||||
func ParseInput(body string, from *User) Message {
|
||||
func ParseInput(body string, from Author) Message {
|
||||
m := NewPublicMsg(body, from)
|
||||
cmd, isCmd := m.ParseCommand()
|
||||
if isCmd {
|
||||
@ -69,10 +74,10 @@ func (m Msg) Timestamp() time.Time {
|
||||
// PublicMsg is any message from a user sent to the room.
|
||||
type PublicMsg struct {
|
||||
Msg
|
||||
from *User
|
||||
from Author
|
||||
}
|
||||
|
||||
func NewPublicMsg(body string, from *User) PublicMsg {
|
||||
func NewPublicMsg(body string, from Author) PublicMsg {
|
||||
return PublicMsg{
|
||||
Msg: Msg{
|
||||
body: body,
|
||||
@ -82,7 +87,7 @@ func NewPublicMsg(body string, from *User) PublicMsg {
|
||||
}
|
||||
}
|
||||
|
||||
func (m PublicMsg) From() *User {
|
||||
func (m PublicMsg) From() Author {
|
||||
return m.from
|
||||
}
|
||||
|
||||
@ -137,10 +142,10 @@ func (m PublicMsg) String() string {
|
||||
// sender to see the emote.
|
||||
type EmoteMsg struct {
|
||||
Msg
|
||||
from *User
|
||||
from Author
|
||||
}
|
||||
|
||||
func NewEmoteMsg(body string, from *User) *EmoteMsg {
|
||||
func NewEmoteMsg(body string, from Author) *EmoteMsg {
|
||||
return &EmoteMsg{
|
||||
Msg: Msg{
|
||||
body: body,
|
||||
@ -161,17 +166,17 @@ func (m EmoteMsg) String() string {
|
||||
// PrivateMsg is a message sent to another user, not shown to anyone else.
|
||||
type PrivateMsg struct {
|
||||
PublicMsg
|
||||
to *User
|
||||
to Author
|
||||
}
|
||||
|
||||
func NewPrivateMsg(body string, from *User, to *User) PrivateMsg {
|
||||
func NewPrivateMsg(body string, from Author, to Author) PrivateMsg {
|
||||
return PrivateMsg{
|
||||
PublicMsg: NewPublicMsg(body, from),
|
||||
to: to,
|
||||
}
|
||||
}
|
||||
|
||||
func (m PrivateMsg) To() *User {
|
||||
func (m PrivateMsg) To() Author {
|
||||
return m.to
|
||||
}
|
||||
|
||||
@ -191,10 +196,10 @@ func (m PrivateMsg) String() string {
|
||||
// to anyone else. Usually in response to something, like /help.
|
||||
type SystemMsg struct {
|
||||
Msg
|
||||
to *User
|
||||
to Author
|
||||
}
|
||||
|
||||
func NewSystemMsg(body string, to *User) *SystemMsg {
|
||||
func NewSystemMsg(body string, to Author) *SystemMsg {
|
||||
return &SystemMsg{
|
||||
Msg: Msg{
|
||||
body: body,
|
||||
@ -215,7 +220,7 @@ func (m *SystemMsg) String() string {
|
||||
return fmt.Sprintf("-> %s", m.body)
|
||||
}
|
||||
|
||||
func (m *SystemMsg) To() *User {
|
||||
func (m *SystemMsg) To() Author {
|
||||
return m.to
|
||||
}
|
||||
|
||||
|
@ -127,12 +127,12 @@ func (t Theme) ID() string {
|
||||
}
|
||||
|
||||
// Colorize name string given some index
|
||||
func (t Theme) ColorName(u *User) string {
|
||||
func (t Theme) ColorName(u Author) string {
|
||||
if t.names == nil {
|
||||
return u.Name()
|
||||
}
|
||||
|
||||
return t.names.Get(u.colorIdx).Format(u.Name())
|
||||
return t.names.Get(u.Color()).Format(u.Name())
|
||||
}
|
||||
|
||||
// Colorize the PM string
|
||||
|
@ -16,10 +16,53 @@ const reHighlight = `\b(%s)\b`
|
||||
|
||||
var ErrUserClosed = errors.New("user closed")
|
||||
|
||||
// User container that knows about writing to an IO screen.
|
||||
type UserScreen struct {
|
||||
*User
|
||||
io.WriteCloser
|
||||
}
|
||||
|
||||
func (u *UserScreen) Close() error {
|
||||
u.User.Close()
|
||||
return u.WriteCloser.Close()
|
||||
}
|
||||
|
||||
// HandleMsg will render the message to the screen, blocking.
|
||||
func (u *UserScreen) HandleMsg(m Message) error {
|
||||
r := u.render(m)
|
||||
_, err := u.Write([]byte(r))
|
||||
if err != nil {
|
||||
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
|
||||
u.User.Close()
|
||||
u.WriteCloser.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Consume message buffer into the handler. Will block, should be called in a
|
||||
// goroutine.
|
||||
func (u *UserScreen) Consume() {
|
||||
for {
|
||||
select {
|
||||
case <-u.done:
|
||||
return
|
||||
case m, ok := <-u.msg:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
u.HandleMsg(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consume one message and stop, mostly for testing
|
||||
// TODO: Stop using it and remove it.
|
||||
func (u *UserScreen) ConsumeOne() Message {
|
||||
return <-u.msg
|
||||
}
|
||||
|
||||
// User definition, implemented set Item interface and io.Writer
|
||||
type User struct {
|
||||
io.WriteCloser
|
||||
|
||||
colorIdx int
|
||||
joined time.Time
|
||||
closeOnce sync.Once
|
||||
@ -29,7 +72,7 @@ type User struct {
|
||||
mu sync.Mutex
|
||||
name string
|
||||
config UserConfig
|
||||
replyTo *User // Set when user gets a /msg, for replying.
|
||||
replyTo Author // Set when user gets a /msg, for replying.
|
||||
}
|
||||
|
||||
func NewUser(name string) *User {
|
||||
@ -45,11 +88,11 @@ func NewUser(name string) *User {
|
||||
return &u
|
||||
}
|
||||
|
||||
func NewUserScreen(name string, screen io.WriteCloser) *User {
|
||||
u := NewUser(name)
|
||||
u.WriteCloser = screen
|
||||
|
||||
return u
|
||||
func NewUserScreen(name string, screen io.WriteCloser) *UserScreen {
|
||||
return &UserScreen{
|
||||
User: NewUser(name),
|
||||
WriteCloser: screen,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Config() UserConfig {
|
||||
@ -70,6 +113,10 @@ func (u *User) ID() string {
|
||||
return SanitizeName(u.name)
|
||||
}
|
||||
|
||||
func (u *User) Color() int {
|
||||
return u.colorIdx
|
||||
}
|
||||
|
||||
func (u *User) Name() string {
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
@ -89,14 +136,14 @@ func (u *User) SetName(name string) {
|
||||
}
|
||||
|
||||
// ReplyTo returns the last user that messaged this user.
|
||||
func (u *User) ReplyTo() *User {
|
||||
func (u *User) ReplyTo() Author {
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.replyTo
|
||||
}
|
||||
|
||||
// SetReplyTo sets the last user to message this user.
|
||||
func (u *User) SetReplyTo(user *User) {
|
||||
func (u *User) SetReplyTo(user Author) {
|
||||
// TODO: Use UserConfig.ReplyTo string
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
@ -112,46 +159,11 @@ func (u *User) setColorIdx(idx int) {
|
||||
// Disconnect user, stop accepting messages
|
||||
func (u *User) Close() {
|
||||
u.closeOnce.Do(func() {
|
||||
u.WriteCloser.Close()
|
||||
// close(u.msg) TODO: Close?
|
||||
close(u.done)
|
||||
})
|
||||
}
|
||||
|
||||
// Consume message buffer into the handler. Will block, should be called in a
|
||||
// goroutine.
|
||||
func (u *User) Consume() {
|
||||
for {
|
||||
select {
|
||||
case <-u.done:
|
||||
return
|
||||
case m, ok := <-u.msg:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
u.HandleMsg(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consume one message and stop, mostly for testing
|
||||
// TODO: Stop using it and remove it.
|
||||
func (u *User) ConsumeOne() Message {
|
||||
return <-u.msg
|
||||
}
|
||||
|
||||
// Check if there are pending messages, used for testing
|
||||
// TODO: Stop using it and remove it.
|
||||
func (u *User) HasMessages() bool {
|
||||
select {
|
||||
case msg := <-u.msg:
|
||||
u.msg <- msg
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// SetHighlight sets the highlighting regular expression to match string.
|
||||
func (u *User) SetHighlight(s string) error {
|
||||
re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
|
||||
@ -177,17 +189,6 @@ func (u *User) render(m Message) string {
|
||||
}
|
||||
}
|
||||
|
||||
// HandleMsg will render the message to the screen, blocking.
|
||||
func (u *User) HandleMsg(m Message) error {
|
||||
r := u.render(m)
|
||||
_, err := u.Write([]byte(r))
|
||||
if err != nil {
|
||||
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
|
||||
u.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Add message to consume by user
|
||||
func (u *User) Send(m Message) error {
|
||||
select {
|
||||
|
@ -78,21 +78,22 @@ func (r *Room) HandleMsg(m message.Message) {
|
||||
go r.HandleMsg(m)
|
||||
}
|
||||
case message.MessageTo:
|
||||
user := m.To()
|
||||
user := m.To().(Member)
|
||||
user.Send(m)
|
||||
default:
|
||||
fromMsg, skip := m.(message.MessageFrom)
|
||||
var skipUser Member
|
||||
if skip {
|
||||
skipUser = fromMsg.From()
|
||||
skipUser = fromMsg.From().(Member)
|
||||
}
|
||||
|
||||
r.history.Add(m)
|
||||
r.Members.Each(func(_ string, item set.Item) (err error) {
|
||||
roomMember := item.Value().(*roomMember)
|
||||
user := roomMember.Member
|
||||
from := fromMsg.From().(Member)
|
||||
|
||||
if fromMsg != nil && roomMember.Ignored.In(fromMsg.From().ID()) {
|
||||
if fromMsg != nil && roomMember.Ignored.In(from.ID()) {
|
||||
// Skip because ignored
|
||||
return
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@ -42,7 +41,7 @@ func TestRoomServe(t *testing.T) {
|
||||
}
|
||||
|
||||
type ScreenedUser struct {
|
||||
user *message.User
|
||||
*message.User
|
||||
screen *MockScreen
|
||||
}
|
||||
|
||||
@ -159,19 +158,6 @@ func expectOutput(t *testing.T, buffer []byte, expected string) {
|
||||
}
|
||||
}
|
||||
|
||||
func sendCommand(cmd string, mock ScreenedUser, room *Room, buffer *[]byte) error {
|
||||
msg, ok := message.NewPublicMsg(cmd, mock.user).ParseCommand()
|
||||
if !ok {
|
||||
return errors.New("cannot parse command message")
|
||||
}
|
||||
|
||||
room.Send(msg)
|
||||
mock.user.HandleMsg(mock.user.ConsumeOne())
|
||||
mock.screen.Read(buffer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRoomJoin(t *testing.T) {
|
||||
var expected, actual []byte
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user