3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-14 07:59:31 +01:00

kick command

This commit is contained in:
Jeremy Latt 2014-02-16 23:29:11 -08:00
parent af5a05f390
commit a9d7f64693
7 changed files with 152 additions and 65 deletions

View File

@ -145,13 +145,7 @@ func (channel *Channel) Part(client *Client, message string) {
} }
channel.Reply(RplPart(client, channel, message)) channel.Reply(RplPart(client, channel, message))
channel.Quit(client)
channel.members.Remove(client)
client.channels.Remove(channel)
for member := range channel.members {
member.RemoveFriend(client)
}
if channel.IsEmpty() { if channel.IsEmpty() {
channel.server.channels.Remove(channel) channel.server.channels.Remove(channel)
@ -305,4 +299,23 @@ func (channel *Channel) Quit(client *Client) {
} }
channel.members.Remove(client) channel.members.Remove(client)
client.channels.Remove(channel)
}
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
if !channel.members.Has(client) {
client.Reply(ErrNotOnChannel(channel))
return
}
if !channel.ClientIsOperator(client) {
client.Reply(ErrChanOPrivIsNeeded(channel))
return
}
if !channel.members.Has(target) {
client.Reply(ErrUserNotInChannel(channel, target))
return
}
channel.Reply(RplKick(channel, client, target, comment))
channel.Quit(target)
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"strings"
"time" "time"
) )
@ -57,9 +58,8 @@ func (client *Client) readCommands() {
if err != nil { if err != nil {
switch err { switch err {
case NotEnoughArgsError: case NotEnoughArgsError:
client.Reply(ErrNeedMoreParams(client.server, line)) parts := strings.SplitN(line, " ", 2)
default: client.Reply(ErrNeedMoreParams(client.server, parts[0]))
client.Reply(ErrUnknownCommand(client.server, line))
} }
continue continue
} }

View File

@ -9,7 +9,7 @@ import (
type editableCommand interface { type editableCommand interface {
Command Command
SetName(string) SetCode(StringCode)
SetClient(*Client) SetClient(*Client)
} }
@ -18,33 +18,34 @@ type parseCommandFunc func([]string) (editableCommand, error)
var ( var (
NotEnoughArgsError = errors.New("not enough arguments") NotEnoughArgsError = errors.New("not enough arguments")
ErrParseCommand = errors.New("failed to parse message") ErrParseCommand = errors.New("failed to parse message")
parseCommandFuncs = map[string]parseCommandFunc{ parseCommandFuncs = map[StringCode]parseCommandFunc{
"AWAY": NewAwayCommand, AWAY: NewAwayCommand,
"CAP": NewCapCommand, CAP: NewCapCommand,
"ISON": NewIsOnCommand, ISON: NewIsOnCommand,
"JOIN": NewJoinCommand, JOIN: NewJoinCommand,
"MODE": NewModeCommand, KICK: NewKickCommand,
"MOTD": NewMOTDCommand, MODE: NewModeCommand,
"NICK": NewNickCommand, MOTD: NewMOTDCommand,
"NOTICE": NewNoticeCommand, NICK: NewNickCommand,
"OPER": NewOperCommand, NOTICE: NewNoticeCommand,
"PART": NewPartCommand, OPER: NewOperCommand,
"PASS": NewPassCommand, PART: NewPartCommand,
"PING": NewPingCommand, PASS: NewPassCommand,
"PONG": NewPongCommand, PING: NewPingCommand,
"PRIVMSG": NewPrivMsgCommand, PONG: NewPongCommand,
"PROXY": NewProxyCommand, PRIVMSG: NewPrivMsgCommand,
"QUIT": NewQuitCommand, PROXY: NewProxyCommand,
"TOPIC": NewTopicCommand, QUIT: NewQuitCommand,
"USER": NewUserMsgCommand, TOPIC: NewTopicCommand,
"WHO": NewWhoCommand, USER: NewUserMsgCommand,
"WHOIS": NewWhoisCommand, WHO: NewWhoCommand,
WHOIS: NewWhoisCommand,
} }
) )
type BaseCommand struct { type BaseCommand struct {
client *Client client *Client
name string code StringCode
} }
func (command *BaseCommand) Client() *Client { func (command *BaseCommand) Client() *Client {
@ -55,12 +56,12 @@ func (command *BaseCommand) SetClient(c *Client) {
command.client = c command.client = c
} }
func (command *BaseCommand) Name() string { func (command *BaseCommand) Code() StringCode {
return command.name return command.code
} }
func (command *BaseCommand) SetName(name string) { func (command *BaseCommand) SetCode(code StringCode) {
command.name = name command.code = code
} }
func (command *BaseCommand) Source() Identifier { func (command *BaseCommand) Source() Identifier {
@ -72,15 +73,15 @@ func (command *BaseCommand) Reply(reply Reply) {
} }
func ParseCommand(line string) (cmd editableCommand, err error) { func ParseCommand(line string) (cmd editableCommand, err error) {
command, args := parseLine(line) code, args := parseLine(line)
constructor := parseCommandFuncs[command] constructor := parseCommandFuncs[code]
if constructor == nil { if constructor == nil {
cmd = NewUnknownCommand(command, args) cmd = NewUnknownCommand(args)
} else { } else {
cmd, err = constructor(args) cmd, err = constructor(args)
} }
if cmd != nil { if cmd != nil {
cmd.SetName(command) cmd.SetCode(code)
} }
return return
} }
@ -102,13 +103,13 @@ func parseArg(line string) (arg string, rest string) {
return return
} }
func parseLine(line string) (command string, args []string) { func parseLine(line string) (command StringCode, args []string) {
args = make([]string, 0) args = make([]string, 0)
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) { for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
args = append(args, arg) args = append(args, arg)
} }
if len(args) > 0 { if len(args) > 0 {
command, args = strings.ToUpper(args[0]), args[1:] command, args = StringCode(strings.ToUpper(args[0])), args[1:]
} }
return return
} }
@ -121,10 +122,10 @@ type UnknownCommand struct {
} }
func (cmd *UnknownCommand) String() string { func (cmd *UnknownCommand) String() string {
return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Name(), cmd.args) return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Code(), cmd.args)
} }
func NewUnknownCommand(command string, args []string) *UnknownCommand { func NewUnknownCommand(args []string) *UnknownCommand {
return &UnknownCommand{ return &UnknownCommand{
args: args, args: args,
} }
@ -739,3 +740,41 @@ func NewNoticeCommand(args []string) (editableCommand, error) {
message: args[1], message: args[1],
}, nil }, nil
} }
type KickCommand struct {
BaseCommand
kicks map[string]string
comment string
}
func (msg *KickCommand) Comment() string {
if msg.comment == "" {
return msg.Source().Nick()
}
return msg.comment
}
func NewKickCommand(args []string) (editableCommand, error) {
if len(args) < 2 {
return nil, NotEnoughArgsError
}
channels := strings.Split(args[0], ",")
users := strings.Split(args[1], ",")
if (len(channels) != len(users)) && (len(users) != 1) {
return nil, NotEnoughArgsError
}
cmd := &KickCommand{
kicks: make(map[string]string),
}
for index, channel := range channels {
if len(users) == 1 {
cmd.kicks[channel] = users[0]
} else {
cmd.kicks[channel] = users[index]
}
}
if len(args) > 2 {
cmd.comment = args[2]
}
return cmd, nil
}

View File

@ -24,18 +24,29 @@ const (
QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
// string codes // string codes
PRIVMSG StringCode = "PRIVMSG" AWAY StringCode = "AWAY"
NOTICE StringCode = "NOTICE" CAP StringCode = "CAP"
NICK StringCode = "NICK"
JOIN StringCode = "JOIN"
PART StringCode = "PART"
MODE StringCode = "MODE"
TOPIC StringCode = "TOPIC"
PING StringCode = "PING"
PONG StringCode = "PONG"
QUIT StringCode = "QUIT"
ERROR StringCode = "ERROR" ERROR StringCode = "ERROR"
INVITE StringCode = "INVITE" INVITE StringCode = "INVITE"
ISON StringCode = "ISON"
JOIN StringCode = "JOIN"
KICK StringCode = "KICK"
MODE StringCode = "MODE"
MOTD StringCode = "MOTD"
NICK StringCode = "NICK"
NOTICE StringCode = "NOTICE"
OPER StringCode = "OPER"
PART StringCode = "PART"
PASS StringCode = "PASS"
PING StringCode = "PING"
PONG StringCode = "PONG"
PRIVMSG StringCode = "PRIVMSG"
PROXY StringCode = "PROXY"
QUIT StringCode = "QUIT"
TOPIC StringCode = "TOPIC"
USER StringCode = "USER"
WHO StringCode = "WHO"
WHOIS StringCode = "WHOIS"
// numeric codes // numeric codes
RPL_WELCOME NumericCode = 1 RPL_WELCOME NumericCode = 1

View File

@ -183,6 +183,11 @@ func RplInviteMsg(channel *Channel, inviter *Client) Reply {
return NewStringReply(inviter, INVITE, channel.name) return NewStringReply(inviter, INVITE, channel.name)
} }
func RplKick(channel *Channel, client *Client, target *Client, comment string) Reply {
return NewStringReply(client, KICK, "%s %s :%s",
channel, target.Nick(), comment)
}
// numeric replies // numeric replies
func RplWelcome(source Identifier, client *Client) Reply { func RplWelcome(source Identifier, client *Client) Reply {
@ -330,9 +335,9 @@ func ErrNickNameInUse(source Identifier, nick string) Reply {
"%s :Nickname is already in use", nick) "%s :Nickname is already in use", nick)
} }
func ErrUnknownCommand(source Identifier, command string) Reply { func ErrUnknownCommand(source Identifier, code StringCode) Reply {
return NewNumericReply(source, ERR_UNKNOWNCOMMAND, return NewNumericReply(source, ERR_UNKNOWNCOMMAND,
"%s :Unknown command", command) "%s :Unknown command", code)
} }
func ErrUsersDontMatch(source Identifier) Reply { func ErrUsersDontMatch(source Identifier) Reply {
@ -381,7 +386,7 @@ func ErrPasswdMismatch(server *Server) Reply {
func ErrNoChanModes(channel *Channel) Reply { func ErrNoChanModes(channel *Channel) Reply {
return NewNumericReply(channel.server, ERR_NOCHANMODES, return NewNumericReply(channel.server, ERR_NOCHANMODES,
"%s :Channel doesn't support modes", channel.name) "%s :Channel doesn't support modes", channel)
} }
func ErrNoPrivileges(server *Server) Reply { func ErrNoPrivileges(server *Server) Reply {
@ -396,20 +401,20 @@ func ErrNoSuchServer(server *Server, target string) Reply {
return NewNumericReply(server, ERR_NOSUCHSERVER, "%s :No such server", target) return NewNumericReply(server, ERR_NOSUCHSERVER, "%s :No such server", target)
} }
func ErrUserNotInChannel(server *Server, nick string, channel *Channel) Reply { func ErrUserNotInChannel(channel *Channel, client *Client) Reply {
return NewNumericReply(server, ERR_USERNOTINCHANNEL, return NewNumericReply(channel.server, ERR_USERNOTINCHANNEL,
"%s %s :They aren't on that channel", nick, channel.name) "%s %s :They aren't on that channel", client.Nick(), channel)
} }
func ErrCannotSendToChan(channel *Channel) Reply { func ErrCannotSendToChan(channel *Channel) Reply {
return NewNumericReply(channel.server, ERR_CANNOTSENDTOCHAN, return NewNumericReply(channel.server, ERR_CANNOTSENDTOCHAN,
"%s :Cannot send to channel", channel.name) "%s :Cannot send to channel", channel)
} }
// <channel> :You're not channel operator // <channel> :You're not channel operator
func ErrChanOPrivIsNeeded(channel *Channel) Reply { func ErrChanOPrivIsNeeded(channel *Channel) Reply {
return NewNumericReply(channel.server, ERR_CHANOPRIVSNEEDED, return NewNumericReply(channel.server, ERR_CHANOPRIVSNEEDED,
"%s :You're not channel operator", channel.name) "%s :You're not channel operator", channel)
} }
func ErrNoMOTD(server *Server) Reply { func ErrNoMOTD(server *Server) Reply {

View File

@ -80,7 +80,7 @@ func (server *Server) ReceiveCommands() {
default: default:
serverCommand, ok := command.(ServerCommand) serverCommand, ok := command.(ServerCommand)
if !ok { if !ok {
client.Reply(ErrUnknownCommand(server, command.Name())) client.Reply(ErrUnknownCommand(server, command.Code()))
continue continue
} }
client.Touch() client.Touch()
@ -542,3 +542,22 @@ func (msg *NoticeCommand) HandleServer(server *Server) {
} }
target.Reply(RplNotice(client, target, msg.message)) target.Reply(RplNotice(client, target, msg.message))
} }
func (msg *KickCommand) HandleServer(server *Server) {
client := msg.Client()
for chname, nickname := range msg.kicks {
channel := server.channels[chname]
if channel == nil {
client.Reply(ErrNoSuchChannel(server, chname))
continue
}
target := server.clients[nickname]
if target == nil {
client.Reply(ErrNoSuchNick(server, nickname))
continue
}
channel.Kick(client, target, msg.Comment())
}
}

View File

@ -171,7 +171,7 @@ type Reply interface {
} }
type Command interface { type Command interface {
Name() string Code() StringCode
Client() *Client Client() *Client
Source() Identifier Source() Identifier
Reply(Reply) Reply(Reply)