diff --git a/irc/channel.go b/irc/channel.go index 362178ad..767509d2 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -18,6 +18,13 @@ type Channel struct { type ChannelSet map[*Channel]bool +func (channels ChannelSet) First() *Channel { + for channel := range channels { + return channel + } + return nil +} + type ChannelCommand interface { Command HandleChannel(channel *Channel) diff --git a/irc/commands.go b/irc/commands.go index 11a9059e..7f4e98b2 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -35,6 +35,7 @@ var ( "QUIT": NewQuitCommand, "TOPIC": NewTopicCommand, "USER": NewUserMsgCommand, + "WHO": NewWhoCommand, "WHOIS": NewWhoisCommand, } ) @@ -485,7 +486,7 @@ type WhoisCommand struct { masks []string } -// [ ] *( "," ) +// WHOIS [ ] *( "," ) func NewWhoisCommand(args []string) (EditableCommand, error) { if len(args) < 1 { return nil, NotEnoughArgsError @@ -506,3 +507,24 @@ func NewWhoisCommand(args []string) (EditableCommand, error) { masks: strings.Split(masks, ","), }, nil } + +type WhoCommand struct { + BaseCommand + mask string + operatorOnly bool +} + +// WHO [ [ "o" ] ] +func NewWhoCommand(args []string) (EditableCommand, error) { + cmd := &WhoCommand{} + + if len(args) > 0 { + cmd.mask = args[0] + } + + if (len(args) > 1) && (args[1] == "o") { + cmd.operatorOnly = true + } + + return cmd, nil +} diff --git a/irc/reply.go b/irc/reply.go index f17da108..a27e20ab 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -239,6 +239,19 @@ func RplChannelModeIs(server *Server, channel *Channel) Reply { channel.name, channel.ModeString()) } +// ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ] +// : +func RplWhoReply(server *Server, channel *Channel, client *Client) Reply { + return NewNumericReply(server, RPL_WHOREPLY, "%s %s %s %s %s H :0 %s", + channel.name, client.username, client.hostname, server.name, client.nick, + client.realname) +} + +// :End of WHO list +func RplEndOfWho(server *Server, name string) Reply { + return NewNumericReply(server, RPL_ENDOFWHO, "%s :End of WHO list", name) +} + // errors (also numeric) func ErrAlreadyRegistered(source Identifier) Reply { diff --git a/irc/server.go b/irc/server.go index 21cdadff..2137baf5 100644 --- a/irc/server.go +++ b/irc/server.go @@ -310,3 +310,32 @@ func (msg *ChannelModeCommand) HandleServer(server *Server) { client.replies <- RplChannelModeIs(server, channel) } + +func whoChannel(client *Client, server *Server, channel *Channel) { + for member := range channel.members { + client.replies <- RplWhoReply(server, channel, member) + } +} + +func (msg *WhoCommand) HandleServer(server *Server) { + client := msg.Client() + // TODO implement wildcard matching + + if msg.mask == "" { + for _, channel := range server.channels { + whoChannel(client, server, channel) + } + } else if IsChannel(msg.mask) { + channel := server.channels[msg.mask] + if channel != nil { + whoChannel(client, server, channel) + } + } else { + mclient := server.clients[msg.mask] + if mclient != nil { + client.replies <- RplWhoReply(server, mclient.channels.First(), mclient) + } + } + + client.replies <- RplEndOfWho(server, msg.mask) +}