mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-07-23 22:15:09 +02:00
chat: Add /mute command for op
This commit is contained in:
parent
b73b45640c
commit
37b101c3c1
@ -477,4 +477,38 @@ func InitCommands(c *Commands) {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
c.Add(Command{
|
||||||
|
Op: true,
|
||||||
|
Prefix: "/mute",
|
||||||
|
PrefixHelp: "USER",
|
||||||
|
Help: "Toggle muting USER, preventing messages from broadcasting.",
|
||||||
|
Handler: func(room *Room, msg message.CommandMsg) error {
|
||||||
|
if !room.IsOp(msg.From()) {
|
||||||
|
return errors.New("must be op")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := msg.Args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("must specify user")
|
||||||
|
}
|
||||||
|
|
||||||
|
member, ok := room.MemberByID(args[0])
|
||||||
|
if !ok {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
setMute := !member.IsMuted()
|
||||||
|
member.SetMute(setMute)
|
||||||
|
id := member.ID()
|
||||||
|
|
||||||
|
if setMute {
|
||||||
|
room.Send(message.NewSystemMsg("Muted: "+id, msg.From()))
|
||||||
|
} else {
|
||||||
|
room.Send(message.NewSystemMsg("Unmuted: "+id, msg.From()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
import stdlog "log"
|
"io"
|
||||||
|
stdlog "log"
|
||||||
|
)
|
||||||
|
|
||||||
var logger *stdlog.Logger
|
var logger *stdlog.Logger
|
||||||
|
|
||||||
|
32
chat/room.go
32
chat/room.go
@ -27,6 +27,23 @@ var ErrInvalidName = errors.New("invalid name")
|
|||||||
type Member struct {
|
type Member struct {
|
||||||
*message.User
|
*message.User
|
||||||
IsOp bool
|
IsOp bool
|
||||||
|
|
||||||
|
// TODO: Move IsOp under mu?
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
isMuted bool // When true, messages should not be broadcasted.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Member) IsMuted() bool {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
return m.isMuted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Member) SetMute(muted bool) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.isMuted = muted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Room definition, also a Set of User Items
|
// Room definition, also a Set of User Items
|
||||||
@ -84,6 +101,20 @@ func (r *Room) HandleMsg(m message.Message) {
|
|||||||
fromID = fromMsg.From().ID()
|
fromID = fromMsg.From().ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fromID != "" {
|
||||||
|
if item, err := r.Members.Get(fromID); err != nil {
|
||||||
|
// Message from a member who is not in the room, this should not happen.
|
||||||
|
logger.Printf("Room received unexpected message from a non-member: %v", m)
|
||||||
|
return
|
||||||
|
} else if member, ok := item.Value().(*Member); ok && member.IsMuted() {
|
||||||
|
// Short circuit message handling for muted users
|
||||||
|
if _, ok = m.(*message.CommandMsg); !ok {
|
||||||
|
member.User.Send(m)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch m := m.(type) {
|
switch m := m.(type) {
|
||||||
case *message.CommandMsg:
|
case *message.CommandMsg:
|
||||||
cmd := *m
|
cmd := *m
|
||||||
@ -150,6 +181,7 @@ func (r *Room) Join(u *message.User) (*Member, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// TODO: Remove user ID from sets, probably referring to a prior user.
|
||||||
r.History(u)
|
r.History(u)
|
||||||
s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), r.Members.Len())
|
s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), r.Members.Len())
|
||||||
r.Send(message.NewAnnounceMsg(s))
|
r.Send(message.NewAnnounceMsg(s))
|
||||||
|
@ -158,6 +158,94 @@ func TestIgnore(t *testing.T) {
|
|||||||
expectOutput(t, buffer, ignored.user.Name()+": hello again!"+message.Newline)
|
expectOutput(t, buffer, ignored.user.Name()+": hello again!"+message.Newline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMute(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)
|
||||||
|
members := make([]*Member, 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
member, err := ch.Join(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
members[i] = member
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
muter := users[0]
|
||||||
|
muted := users[1]
|
||||||
|
other := users[2]
|
||||||
|
|
||||||
|
members[0].IsOp = true
|
||||||
|
|
||||||
|
// test muting unexisting user
|
||||||
|
if err := sendCommand("/mute test", muter, ch, &buffer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectOutput(t, buffer, "-> Err: user not found"+message.Newline)
|
||||||
|
|
||||||
|
// test muting by non-op
|
||||||
|
if err := sendCommand("/mute "+muted.user.Name(), other, ch, &buffer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectOutput(t, buffer, "-> Err: must be op"+message.Newline)
|
||||||
|
|
||||||
|
// test muting existing user
|
||||||
|
if err := sendCommand("/mute "+muted.user.Name(), muter, ch, &buffer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectOutput(t, buffer, "-> Muted: "+muted.user.Name()+message.Newline)
|
||||||
|
|
||||||
|
if got, want := members[1].IsMuted(), true; got != want {
|
||||||
|
t.Error("muted user failed to set mute flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// when an emote is sent by a muted user, it should not be displayed for anyone
|
||||||
|
ch.HandleMsg(message.NewPublicMsg("hello!", muted.user))
|
||||||
|
ch.HandleMsg(message.NewEmoteMsg("is crying", muted.user))
|
||||||
|
|
||||||
|
if muter.user.HasMessages() {
|
||||||
|
muter.user.HandleMsg(muter.user.ConsumeOne())
|
||||||
|
muter.screen.Read(&buffer)
|
||||||
|
t.Errorf("muter should not have messages: %s", buffer)
|
||||||
|
}
|
||||||
|
if other.user.HasMessages() {
|
||||||
|
other.user.HandleMsg(other.user.ConsumeOne())
|
||||||
|
other.screen.Read(&buffer)
|
||||||
|
t.Errorf("other should not have messages: %s", buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test unmuting
|
||||||
|
if err := sendCommand("/mute "+muted.user.Name(), muter, ch, &buffer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectOutput(t, buffer, "-> Unmuted: "+muted.user.Name()+message.Newline)
|
||||||
|
|
||||||
|
ch.HandleMsg(message.NewPublicMsg("hello again!", muted.user))
|
||||||
|
other.user.HandleMsg(other.user.ConsumeOne())
|
||||||
|
other.screen.Read(&buffer)
|
||||||
|
expectOutput(t, buffer, muted.user.Name()+": hello again!"+message.Newline)
|
||||||
|
}
|
||||||
|
|
||||||
func expectOutput(t *testing.T, buffer []byte, expected string) {
|
func expectOutput(t *testing.T, buffer []byte, expected string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user