3
0
mirror of https://github.com/ergochat/ergo.git synced 2025-02-27 11:00:43 +01:00

implement channel modes and messages properly

This commit is contained in:
Jeremy Latt 2014-02-15 19:49:20 -08:00
parent 0bfa2fb98f
commit 488b2ccf8f
6 changed files with 156 additions and 80 deletions

View File

@ -84,6 +84,14 @@ func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
} }
} }
func IsPrivMsg(reply Reply) bool {
strReply, ok := reply.(*StringReply)
if !ok {
return false
}
return strReply.code == "PRIVMSG"
}
func (channel *Channel) receiveReplies(replies <-chan Reply) { func (channel *Channel) receiveReplies(replies <-chan Reply) {
for reply := range replies { for reply := range replies {
if channel.destroyed { if channel.destroyed {
@ -98,9 +106,10 @@ func (channel *Channel) receiveReplies(replies <-chan Reply) {
} }
channel.mutex.Lock() channel.mutex.Lock()
for client := range channel.members { for client := range channel.members {
if reply.Source() != Identifier(client) { if IsPrivMsg(reply) && (reply.Source() == Identifier(client)) {
client.Reply(reply) continue
} }
client.Reply(reply)
} }
channel.mutex.Unlock() channel.mutex.Unlock()
} }
@ -187,9 +196,7 @@ func (channel *Channel) Join(client *Client) {
channel.mutex.Unlock() channel.mutex.Unlock()
client.channels.Add(channel) client.channels.Add(channel)
reply := RplJoin(client, channel) channel.Reply(RplJoin(client, channel))
client.Reply(reply)
channel.Reply(reply)
channel.GetTopic(client) channel.GetTopic(client)
channel.GetUsers(client) channel.GetUsers(client)
} }
@ -216,9 +223,7 @@ func (m *PartCommand) HandleChannel(channel *Channel) {
return return
} }
reply := RplPart(client, channel, m.Message()) channel.Reply(RplPart(client, channel, m.Message()))
client.Reply(reply)
channel.Reply(reply)
channel.members.Remove(client) channel.members.Remove(client)
client.channels.Remove(channel) client.channels.Remove(channel)
@ -245,9 +250,7 @@ func (m *TopicCommand) HandleChannel(channel *Channel) {
channel.topic = m.topic channel.topic = m.topic
channel.GetTopic(client) channel.GetTopic(client)
reply := RplTopicMsg(client, channel) channel.Reply(RplTopicMsg(client, channel))
client.Reply(reply)
channel.Reply(reply)
return return
} }
@ -267,10 +270,18 @@ func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
func (msg *ChannelModeCommand) HandleChannel(channel *Channel) { func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
client := msg.Client() client := msg.Client()
for _, modeOp := range msg.modeOps { if len(msg.changes) == 0 {
switch modeOp.mode { client.Reply(RplChannelModeIs(channel))
return
}
changes := make(ChannelModeChanges, 0)
for _, change := range msg.changes {
switch change.mode {
case BanMask: case BanMask:
// TODO add/remove // TODO add/remove
for _, banMask := range channel.banList { for _, banMask := range channel.banList {
client.Reply(RplBanList(channel, banMask)) client.Reply(RplBanList(channel, banMask))
} }
@ -282,12 +293,14 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
continue continue
} }
switch modeOp.op { switch change.op {
case Add: case Add:
channel.flags[modeOp.mode] = true channel.flags[change.mode] = true
changes = append(changes, change)
case Remove: case Remove:
delete(channel.flags, modeOp.mode) delete(channel.flags, change.mode)
changes = append(changes, change)
} }
case Key: case Key:
@ -296,34 +309,33 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
continue continue
} }
switch modeOp.op { switch change.op {
case Add: case Add:
if modeOp.arg == "" { if change.arg == "" {
// TODO err reply // TODO err reply
continue continue
} }
channel.key = modeOp.arg channel.key = change.arg
changes = append(changes, change)
case Remove: case Remove:
channel.key = "" channel.key = ""
changes = append(changes, change)
} }
}
mmode := ChannelMemberMode(modeOp.mode)
switch mmode {
case ChannelOperator, Voice: case ChannelOperator, Voice:
if !channel.ClientIsOperator(client) { if !channel.ClientIsOperator(client) {
client.Reply(ErrChanOPrivIsNeeded(channel)) client.Reply(ErrChanOPrivIsNeeded(channel))
continue continue
} }
if modeOp.arg == "" { if change.arg == "" {
// TODO err reply // TODO err reply
continue continue
} }
target := channel.server.clients[modeOp.arg] target := channel.server.clients[change.arg]
if target == nil { if target == nil {
// TODO err reply // TODO err reply
continue continue
@ -334,16 +346,21 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
continue continue
} }
switch modeOp.op { switch change.op {
case Add: case Add:
channel.members[target][mmode] = true channel.members[target][change.mode] = true
changes = append(changes, change)
case Remove: case Remove:
channel.members[target][mmode] = false channel.members[target][change.mode] = false
changes = append(changes, change)
} }
} }
} }
client.Reply(RplChannelModeIs(channel)) if len(changes) > 0 {
channel.Reply(RplChannelMode(client, channel, changes))
}
} }
func (m *NoticeCommand) HandleChannel(channel *Channel) { func (m *NoticeCommand) HandleChannel(channel *Channel) {

View File

@ -402,6 +402,26 @@ func (change *ModeChange) String() string {
return fmt.Sprintf("%s%s", change.op, change.mode) return fmt.Sprintf("%s%s", change.op, change.mode)
} }
type ModeChanges []ModeChange
func (changes ModeChanges) String() string {
if len(changes) == 0 {
return ""
}
op := changes[0].op
str := changes[0].op.String()
for _, change := range changes {
if change.op == op {
str += change.mode.String()
} else {
op = change.op
str += " " + change.op.String()
}
}
return str
}
type ModeCommand struct { type ModeCommand struct {
BaseCommand BaseCommand
nickname string nickname string
@ -436,27 +456,47 @@ func (cmd *ModeCommand) String() string {
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes) return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
} }
type ChannelModeOp struct { type ChannelModeChange struct {
mode ChannelMode mode ChannelMode
op ModeOp op ModeOp
arg string arg string
} }
func (op *ChannelModeOp) String() string { func (op *ChannelModeChange) String() string {
return fmt.Sprintf("{%s %s %s}", op.op, op.mode, op.arg) return fmt.Sprintf("{%s %s %s}", op.op, op.mode, op.arg)
} }
type ChannelModeChanges []ChannelModeChange
func (changes ChannelModeChanges) String() string {
if len(changes) == 0 {
return ""
}
str := "+"
if changes[0].op == Remove {
str = "-"
}
for _, change := range changes {
str += change.mode.String()
}
for _, change := range changes {
str += " " + change.arg
}
return str
}
type ChannelModeCommand struct { type ChannelModeCommand struct {
BaseCommand BaseCommand
channel string channel string
modeOps []ChannelModeOp changes ChannelModeChanges
} }
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> ) // MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
func NewChannelModeCommand(args []string) (editableCommand, error) { func NewChannelModeCommand(args []string) (editableCommand, error) {
cmd := &ChannelModeCommand{ cmd := &ChannelModeCommand{
channel: args[0], channel: args[0],
modeOps: make([]ChannelModeOp, 0), changes: make(ChannelModeChanges, 0),
} }
args = args[1:] args = args[1:]
@ -472,18 +512,18 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
skipArgs := 1 skipArgs := 1
for _, mode := range modeArg { for _, mode := range modeArg {
modeOp := ChannelModeOp{ change := ChannelModeChange{
mode: ChannelMode(mode), mode: ChannelMode(mode),
op: op, op: op,
} }
switch modeOp.mode { switch change.mode {
case Key, BanMask, ExceptionMask, InviteMask, UserLimit: case Key, BanMask, ExceptionMask, InviteMask, UserLimit:
if len(args) > skipArgs { if len(args) > skipArgs {
modeOp.arg = args[skipArgs] change.arg = args[skipArgs]
skipArgs += 1 skipArgs += 1
} }
} }
cmd.modeOps = append(cmd.modeOps, modeOp) cmd.changes = append(cmd.changes, change)
} }
args = args[skipArgs:] args = args[skipArgs:]
} }
@ -492,7 +532,7 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
} }
func (msg *ChannelModeCommand) String() string { func (msg *ChannelModeCommand) String() string {
return fmt.Sprintf("MODE(channel=%s, modeOps=%s)", msg.channel, msg.modeOps) return fmt.Sprintf("MODE(channel=%s, changes=%s)", msg.channel, msg.changes)
} }
func NewModeCommand(args []string) (editableCommand, error) { func NewModeCommand(args []string) (editableCommand, error) {

View File

@ -173,24 +173,23 @@ const (
ServerNotice UserMode = 's' ServerNotice UserMode = 's'
WallOps UserMode = 'w' WallOps UserMode = 'w'
Anonymous ChannelMode = 'a' // flag Anonymous ChannelMode = 'a' // flag
BanMask ChannelMode = 'b' // arg BanMask ChannelMode = 'b' // arg
ExceptionMask ChannelMode = 'e' // arg ChannelCreator ChannelMode = 'O' // flag
InviteMask ChannelMode = 'I' // arg ChannelOperator ChannelMode = 'o' // arg
InviteOnly ChannelMode = 'i' // flag ExceptionMask ChannelMode = 'e' // arg
Key ChannelMode = 'k' // flag arg InviteMask ChannelMode = 'I' // arg
Moderated ChannelMode = 'm' // flag InviteOnly ChannelMode = 'i' // flag
NoOutside ChannelMode = 'n' // flag Key ChannelMode = 'k' // flag arg
OpOnlyTopic ChannelMode = 't' // flag Moderated ChannelMode = 'm' // flag
Private ChannelMode = 'p' // flag NoOutside ChannelMode = 'n' // flag
Quiet ChannelMode = 'q' // flag OpOnlyTopic ChannelMode = 't' // flag
ReOp ChannelMode = 'r' // flag Private ChannelMode = 'p' // flag
Secret ChannelMode = 's' // flag Quiet ChannelMode = 'q' // flag
UserLimit ChannelMode = 'l' // flag arg ReOp ChannelMode = 'r' // flag
Secret ChannelMode = 's' // flag
ChannelCreator ChannelMemberMode = 'O' // flag UserLimit ChannelMode = 'l' // flag arg
ChannelOperator ChannelMemberMode = 'o' // arg Voice ChannelMode = 'v' // arg
Voice ChannelMemberMode = 'v' // arg
) )
const ( const (

View File

@ -144,11 +144,20 @@ func RplJoin(client *Client, channel *Channel) Reply {
} }
func RplPart(client *Client, channel *Channel, message string) Reply { func RplPart(client *Client, channel *Channel, message string) Reply {
return NewStringReply(client, "PART", "%s :%s", channel.name, message) return NewStringReply(client, "PART", "%s :%s", channel, message)
}
func RplMode(client *Client, changes ModeChanges) Reply {
return NewStringReply(client, "MODE", "%s :%s", client.Nick(), changes)
}
func RplChannelMode(client *Client, channel *Channel,
changes ChannelModeChanges) Reply {
return NewStringReply(client, "MODE", "%s %s", channel, changes)
} }
func RplTopicMsg(source Identifier, channel *Channel) Reply { func RplTopicMsg(source Identifier, channel *Channel) Reply {
return NewStringReply(source, "TOPIC", "%s :%s", channel.name, channel.topic) return NewStringReply(source, "TOPIC", "%s :%s", channel, channel.topic)
} }
func RplPing(server *Server, target Identifier) Reply { func RplPing(server *Server, target Identifier) Reply {
@ -207,9 +216,11 @@ func RplTopic(channel *Channel) Reply {
"%s :%s", channel.name, channel.topic) "%s :%s", channel.name, channel.topic)
} }
// <nick> <channel>
// NB: correction in errata
func RplInvitingMsg(channel *Channel, invitee *Client) Reply { func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
return NewNumericReply(channel.server, RPL_INVITING, return NewNumericReply(channel.server, RPL_INVITING,
"%s %s", channel.name, invitee.Nick()) "%s %s", invitee.Nick(), channel.name)
} }
func RplNamReply(channel *Channel, names []string) *NumericReply { func RplNamReply(channel *Channel, names []string) *NumericReply {
@ -238,7 +249,7 @@ func RplEndOfWhois(server *Server) Reply {
func RplChannelModeIs(channel *Channel) Reply { func RplChannelModeIs(channel *Channel) Reply {
return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s", return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s",
channel.name, channel.ModeString()) channel, channel.ModeString())
} }
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ] // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]

View File

@ -270,7 +270,6 @@ func (m *NickCommand) HandleRegServer(s *Server) {
client.nick = m.nickname client.nick = m.nickname
s.clients.Add(client) s.clients.Add(client)
client.Reply(RplNick(client, m.nickname))
s.tryRegister(client) s.tryRegister(client)
} }
@ -414,22 +413,31 @@ func (m *PrivMsgCommand) HandleServer(s *Server) {
func (m *ModeCommand) HandleServer(s *Server) { func (m *ModeCommand) HandleServer(s *Server) {
client := m.Client() client := m.Client()
target := s.clients[m.nickname] target := s.clients[m.nickname]
if client == target { // TODO other auth
for _, change := range m.changes { if client != target {
if change.mode == Invisible { client.Reply(ErrUsersDontMatch(s))
switch change.op {
case Add:
client.invisible = true
case Remove:
client.invisible = false
}
}
}
client.Reply(RplUModeIs(s, client))
return return
} }
client.Reply(ErrUsersDontMatch(s)) changes := make(ModeChanges, 0)
for _, change := range m.changes {
if change.mode == Invisible {
switch change.op {
case Add:
client.invisible = true
changes = append(changes, change)
case Remove:
client.invisible = false
changes = append(changes, change)
}
}
}
if len(changes) > 0 {
client.Reply(RplMode(client, changes))
}
} }
func (m *WhoisCommand) HandleServer(server *Server) { func (m *WhoisCommand) HandleServer(server *Server) {

View File

@ -15,6 +15,10 @@ type Mask string
// add, remove, list modes // add, remove, list modes
type ModeOp rune type ModeOp rune
func (op ModeOp) String() string {
return string(op)
}
// user mode flags // user mode flags
type UserMode rune type UserMode rune
@ -37,9 +41,6 @@ func (mode ChannelMode) String() string {
return fmt.Sprintf("%c", mode) return fmt.Sprintf("%c", mode)
} }
// user-channel mode flags
type ChannelMemberMode rune
type ChannelNameMap map[string]*Channel type ChannelNameMap map[string]*Channel
func (channels ChannelNameMap) Add(channel *Channel) error { func (channels ChannelNameMap) Add(channel *Channel) error {
@ -84,19 +85,19 @@ func (clients ClientNameMap) Remove(client *Client) error {
return nil return nil
} }
type ChannelMemberModeSet map[ChannelMemberMode]bool type ChannelModeSet map[ChannelMode]bool
type ClientSet map[*Client]ChannelMemberModeSet type ClientSet map[*Client]ChannelModeSet
func (clients ClientSet) Add(client *Client) { func (clients ClientSet) Add(client *Client) {
clients[client] = make(ChannelMemberModeSet) clients[client] = make(ChannelModeSet)
} }
func (clients ClientSet) Remove(client *Client) { func (clients ClientSet) Remove(client *Client) {
delete(clients, client) delete(clients, client)
} }
func (clients ClientSet) HasMode(client *Client, mode ChannelMemberMode) bool { func (clients ClientSet) HasMode(client *Client, mode ChannelMode) bool {
modes, ok := clients[client] modes, ok := clients[client]
if !ok { if !ok {
return false return false