mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-09-22 10:50:18 -04:00
Add /ignore
//unignore
commands
This commit is contained in:
parent
2abe368022
commit
e3a99d41d6
@ -240,4 +240,64 @@ func InitCommands(c *Commands) {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
c.Add(Command{
|
||||
Prefix: "/ignore",
|
||||
PrefixHelp: "[USER]",
|
||||
Help: "Ignore messages from USER, list ignored users without parameters.",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
id := strings.TrimSpace(strings.TrimLeft(msg.Body(), "/ignore"))
|
||||
if id == "" {
|
||||
ignored := msg.From().IgnoredNames()
|
||||
|
||||
var systemMsg string
|
||||
if len(ignored) == 0 {
|
||||
systemMsg = "0 users ignored."
|
||||
} else {
|
||||
systemMsg = fmt.Sprintf("%d ignored: %s", len(ignored), strings.Join(ignored, ", "))
|
||||
}
|
||||
|
||||
room.Send(message.NewSystemMsg(systemMsg, msg.From()))
|
||||
return nil
|
||||
}
|
||||
|
||||
target, ok := room.MemberById(id)
|
||||
if !ok {
|
||||
return fmt.Errorf("user %s not found.", id)
|
||||
}
|
||||
|
||||
// Don't ignore yourself
|
||||
if target.Id() == msg.From().Id() {
|
||||
return errors.New("cannot ignore self.")
|
||||
}
|
||||
|
||||
err := msg.From().Ignore(target.Id())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
room.Send(message.NewSystemMsg(fmt.Sprintf("%s is now being ignored.", target.Name()), msg.From()))
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
c.Add(Command{
|
||||
Prefix: "/unignore",
|
||||
PrefixHelp: "[USER]",
|
||||
Help: "Stop ignoring USER.",
|
||||
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||
id := strings.TrimSpace(strings.TrimLeft(msg.Body(), "/unignore"))
|
||||
if id == "" {
|
||||
return errors.New("missing user id")
|
||||
}
|
||||
|
||||
err := msg.From().Unignore(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
room.Send(message.NewSystemMsg(fmt.Sprintf("%s is not ignored anymore.", id), msg.From()))
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -24,10 +24,13 @@ type User struct {
|
||||
joined time.Time
|
||||
msg chan Message
|
||||
done chan struct{}
|
||||
ignored []string
|
||||
|
||||
replyTo *User // Set when user gets a /msg, for replying.
|
||||
screen io.WriteCloser
|
||||
closeOnce sync.Once
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewUser(identity Identifier) *User {
|
||||
@ -114,6 +117,17 @@ func (u *User) ConsumeOne() Message {
|
||||
return <-u.msg
|
||||
}
|
||||
|
||||
// Check if there are pending messages, used for testing
|
||||
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))
|
||||
@ -161,6 +175,65 @@ func (u *User) Send(m Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *User) Ignore(id string) error {
|
||||
if id == "" {
|
||||
return errors.New("user is nil.")
|
||||
}
|
||||
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
|
||||
for _, userId := range u.ignored {
|
||||
if userId == id {
|
||||
return errors.New("user already ignored.")
|
||||
}
|
||||
}
|
||||
|
||||
u.ignored = append(u.ignored, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *User) Unignore(id string) error {
|
||||
if id == "" {
|
||||
return errors.New("user is nil.")
|
||||
}
|
||||
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
|
||||
for i, userId := range u.ignored {
|
||||
if userId == id {
|
||||
u.ignored = append(u.ignored[:i], u.ignored[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("user not found or not currently ignored.")
|
||||
}
|
||||
|
||||
func (u *User) IgnoredNames() []string {
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
|
||||
names := make([]string, len(u.ignored))
|
||||
for i := range u.ignored {
|
||||
names[i] = u.ignored[i]
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (u *User) IsIgnoring(id string) bool {
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
|
||||
for _, userId := range u.ignored {
|
||||
if userId == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Container for per-user configurations.
|
||||
type UserConfig struct {
|
||||
Highlight *regexp.Regexp
|
||||
|
@ -97,6 +97,12 @@ func (r *Room) HandleMsg(m message.Message) {
|
||||
r.history.Add(m)
|
||||
r.Members.Each(func(u identified) {
|
||||
user := u.(*Member).User
|
||||
|
||||
if fromMsg != nil && user.IsIgnoring(fromMsg.From().Id()) {
|
||||
// Skip because ignored
|
||||
return
|
||||
}
|
||||
|
||||
if skip && skipUser == user {
|
||||
// Skip
|
||||
return
|
||||
|
@ -1,8 +1,11 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shazow/ssh-chat/chat/message"
|
||||
)
|
||||
@ -40,6 +43,134 @@ func TestRoomServe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type ScreenedUser struct {
|
||||
user *message.User
|
||||
screen *MockScreen
|
||||
}
|
||||
|
||||
func TestIgnore(t *testing.T) {
|
||||
var buffer []byte
|
||||
|
||||
ch := NewRoom()
|
||||
go ch.Serve()
|
||||
defer ch.Close()
|
||||
|
||||
// Create 3 users, join the room and clear their screen buffers
|
||||
users := make([]ScreenedUser, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
screen := &MockScreen{}
|
||||
user := message.NewUserScreen(message.SimpleId(fmt.Sprintf("user%d", i)), screen)
|
||||
users[i] = ScreenedUser{
|
||||
user: user,
|
||||
screen: screen,
|
||||
}
|
||||
|
||||
_, err := ch.Join(user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
for i := 0; i < 3; i++ {
|
||||
u.user.HandleMsg(u.user.ConsumeOne())
|
||||
u.screen.Read(&buffer)
|
||||
}
|
||||
}
|
||||
|
||||
// Use some handy variable names for distinguish between roles
|
||||
ignorer := users[0]
|
||||
ignored := users[1]
|
||||
other := users[2]
|
||||
|
||||
// test ignoring unexisting user
|
||||
if err := sendCommand("/ignore test", ignorer, ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> Err: user test not found."+message.Newline)
|
||||
|
||||
// test ignoring existing user
|
||||
if err := sendCommand("/ignore "+ignored.user.Name(), ignorer, ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> "+ignored.user.Name()+" is now being ignored."+message.Newline)
|
||||
|
||||
// ignoring the same user twice returns an error message and doesn't add the user twice
|
||||
if err := sendCommand("/ignore "+ignored.user.Name(), ignorer, ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> Err: user already ignored."+message.Newline)
|
||||
if names := ignorer.user.IgnoredNames(); len(names) != 1 {
|
||||
t.Fatalf("should have %d ignored users, has %d", 1, len(names))
|
||||
}
|
||||
|
||||
// when a message is sent from the ignored user, it is delivered to non-ignoring users
|
||||
ch.Send(message.NewPublicMsg("hello", ignored.user))
|
||||
other.user.HandleMsg(other.user.ConsumeOne())
|
||||
other.screen.Read(&buffer)
|
||||
expectOutput(t, buffer, ignored.user.Name()+": hello"+message.Newline)
|
||||
|
||||
// ensure ignorer doesn't have received any message
|
||||
if ignorer.user.HasMessages() {
|
||||
t.Fatal("should not have messages")
|
||||
}
|
||||
|
||||
// `/ignore` returns a list of ignored users
|
||||
if err := sendCommand("/ignore", ignorer, ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> 1 ignored: "+ignored.user.Name()+message.Newline)
|
||||
|
||||
// `/unignore [USER]` removes the user from ignored ones
|
||||
if err := sendCommand("/unignore "+ignored.user.Name(), users[0], ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> "+ignored.user.Name()+" is not ignored anymore."+message.Newline)
|
||||
|
||||
if err := sendCommand("/ignore", users[0], ch, &buffer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectOutput(t, buffer, "-> 0 users ignored."+message.Newline)
|
||||
|
||||
if names := ignorer.user.IgnoredNames(); len(names) != 0 {
|
||||
t.Fatalf("should have %d ignored users, has %d", 0, len(names))
|
||||
}
|
||||
|
||||
// after unignoring a user, its messages can be received again
|
||||
ch.Send(message.NewPublicMsg("hello again!", ignored.user))
|
||||
|
||||
// give some time for the channel to get the message
|
||||
time.Sleep(50)
|
||||
|
||||
// ensure ignorer has received the message
|
||||
if !ignorer.user.HasMessages() {
|
||||
t.Fatal("should have messages")
|
||||
}
|
||||
ignorer.user.HandleMsg(ignorer.user.ConsumeOne())
|
||||
ignorer.screen.Read(&buffer)
|
||||
expectOutput(t, buffer, ignored.user.Name()+": hello again!"+message.Newline)
|
||||
}
|
||||
|
||||
func expectOutput(t *testing.T, buffer []byte, expected string) {
|
||||
bytes := []byte(expected)
|
||||
if !reflect.DeepEqual(buffer, bytes) {
|
||||
t.Errorf("Got: %q; Expected: %q", buffer, expected)
|
||||
}
|
||||
}
|
||||
|
||||
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