3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-23 03:02:48 +01:00
ergo/irc/commands.go

810 lines
15 KiB
Go
Raw Normal View History

package irc
2012-12-09 21:51:50 +01:00
import (
"errors"
2013-05-11 22:55:01 +02:00
"fmt"
2012-12-09 21:51:50 +01:00
"strconv"
"strings"
)
type editableCommand interface {
Command
2014-02-17 08:29:11 +01:00
SetCode(StringCode)
2014-02-09 08:51:51 +01:00
SetClient(*Client)
}
type parseCommandFunc func([]string) (editableCommand, error)
var (
2013-05-09 20:05:10 +02:00
NotEnoughArgsError = errors.New("not enough arguments")
ErrParseCommand = errors.New("failed to parse message")
2014-02-17 08:29:11 +01:00
parseCommandFuncs = map[StringCode]parseCommandFunc{
AWAY: NewAwayCommand,
CAP: NewCapCommand,
ISON: NewIsOnCommand,
JOIN: NewJoinCommand,
KICK: NewKickCommand,
2014-02-17 08:51:27 +01:00
LIST: NewListCommand,
2014-02-17 08:29:11 +01:00
MODE: NewModeCommand,
MOTD: NewMOTDCommand,
NICK: NewNickCommand,
NOTICE: NewNoticeCommand,
OPER: NewOperCommand,
PART: NewPartCommand,
PASS: NewPassCommand,
PING: NewPingCommand,
PONG: NewPongCommand,
PRIVMSG: NewPrivMsgCommand,
PROXY: NewProxyCommand,
QUIT: NewQuitCommand,
TOPIC: NewTopicCommand,
USER: NewUserMsgCommand,
WHO: NewWhoCommand,
WHOIS: NewWhoisCommand,
}
)
2013-05-09 20:05:10 +02:00
type BaseCommand struct {
client *Client
2014-02-17 08:29:11 +01:00
code StringCode
2013-05-09 20:05:10 +02:00
}
func (command *BaseCommand) Client() *Client {
2013-05-11 22:55:01 +02:00
return command.client
2013-05-09 20:05:10 +02:00
}
2014-02-09 08:51:51 +01:00
func (command *BaseCommand) SetClient(c *Client) {
command.client = c
2013-05-09 20:05:10 +02:00
}
2014-02-17 08:29:11 +01:00
func (command *BaseCommand) Code() StringCode {
return command.code
2014-02-15 03:28:36 +01:00
}
2014-02-17 08:29:11 +01:00
func (command *BaseCommand) SetCode(code StringCode) {
command.code = code
2014-02-15 03:28:36 +01:00
}
func (command *BaseCommand) Source() Identifier {
2014-02-15 03:28:36 +01:00
return command.Client()
2013-06-03 07:07:50 +02:00
}
func (command *BaseCommand) Reply(reply Reply) {
2014-02-09 17:48:11 +01:00
command.client.Reply(reply)
}
2014-02-15 03:28:36 +01:00
func ParseCommand(line string) (cmd editableCommand, err error) {
2014-02-17 08:29:11 +01:00
code, args := parseLine(line)
constructor := parseCommandFuncs[code]
if constructor == nil {
2014-02-17 08:29:11 +01:00
cmd = NewUnknownCommand(args)
2014-02-15 03:28:36 +01:00
} else {
cmd, err = constructor(args)
}
2014-02-15 03:28:36 +01:00
if cmd != nil {
2014-02-17 08:29:11 +01:00
cmd.SetCode(code)
2014-02-15 03:28:36 +01:00
}
return
}
func parseArg(line string) (arg string, rest string) {
if line == "" {
return
}
if strings.HasPrefix(line, ":") {
arg = line[1:]
} else {
parts := strings.SplitN(line, " ", 2)
arg = parts[0]
if len(parts) > 1 {
rest = parts[1]
}
}
return
}
2014-02-17 08:29:11 +01:00
func parseLine(line string) (command StringCode, args []string) {
args = make([]string, 0)
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
args = append(args, arg)
}
2014-02-17 07:22:46 +01:00
if len(args) > 0 {
2014-02-17 08:29:11 +01:00
command, args = StringCode(strings.ToUpper(args[0])), args[1:]
2014-02-17 07:22:46 +01:00
}
return
}
// <command> [args...]
type UnknownCommand struct {
BaseCommand
2014-02-15 03:28:36 +01:00
args []string
}
func (cmd *UnknownCommand) String() string {
2014-02-17 08:29:11 +01:00
return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Code(), cmd.args)
}
2014-02-17 08:29:11 +01:00
func NewUnknownCommand(args []string) *UnknownCommand {
return &UnknownCommand{
2014-02-15 03:28:36 +01:00
args: args,
}
}
2012-12-09 21:51:50 +01:00
2012-12-13 08:27:17 +01:00
// PING <server1> [ <server2> ]
type PingCommand struct {
BaseCommand
server string
server2 string
}
func (cmd *PingCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("PING(server=%s, server2=%s)", cmd.server, cmd.server2)
}
func NewPingCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) < 1 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
msg := &PingCommand{
2013-06-03 07:07:50 +02:00
server: args[0],
2012-12-13 08:27:17 +01:00
}
2012-12-09 21:51:50 +01:00
if len(args) > 1 {
msg.server2 = args[1]
}
return msg, nil
}
2012-12-13 08:27:17 +01:00
// PONG <server> [ <server2> ]
type PongCommand struct {
BaseCommand
server1 string
server2 string
}
func (cmd *PongCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("PONG(server1=%s, server2=%s)", cmd.server1, cmd.server2)
}
func NewPongCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) < 1 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
message := &PongCommand{
2013-06-03 07:07:50 +02:00
server1: args[0],
}
2012-12-09 21:51:50 +01:00
if len(args) > 1 {
message.server2 = args[1]
}
return message, nil
}
2012-12-10 05:24:53 +01:00
// PASS <password>
type PassCommand struct {
BaseCommand
2012-12-10 05:24:53 +01:00
password string
}
func (cmd *PassCommand) String() string {
return fmt.Sprintf("PASS(password=%s)", cmd.password)
}
func NewPassCommand(args []string) (editableCommand, error) {
2012-12-10 05:24:53 +01:00
if len(args) < 1 {
return nil, NotEnoughArgsError
}
return &PassCommand{
2013-06-03 07:07:50 +02:00
password: args[0],
}, nil
2012-12-10 05:24:53 +01:00
}
2012-12-13 08:27:17 +01:00
// NICK <nickname>
type NickCommand struct {
BaseCommand
nickname string
}
func (m *NickCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("NICK(nickname=%s)", m.nickname)
}
func NewNickCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) != 1 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
return &NickCommand{
2013-06-03 07:07:50 +02:00
nickname: args[0],
2012-12-13 08:27:17 +01:00
}, nil
2012-12-09 21:51:50 +01:00
}
2012-12-13 08:27:17 +01:00
// USER <user> <mode> <unused> <realname>
type UserMsgCommand struct {
BaseCommand
user string
mode uint8
unused string
realname string
}
func (cmd *UserMsgCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("USER(user=%s, mode=%o, unused=%s, realname=%s)",
cmd.user, cmd.mode, cmd.unused, cmd.realname)
}
func NewUserMsgCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) != 4 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
msg := &UserMsgCommand{
2013-06-03 07:07:50 +02:00
user: args[0],
unused: args[2],
realname: args[3],
2012-12-09 21:51:50 +01:00
}
mode, err := strconv.ParseUint(args[1], 10, 8)
if err == nil {
msg.mode = uint8(mode)
}
return msg, nil
}
// QUIT [ <Quit Command> ]
type QuitCommand struct {
BaseCommand
message string
}
func (cmd *QuitCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("QUIT(message=%s)", cmd.message)
}
func NewQuitCommand(args []string) (editableCommand, error) {
2013-06-03 07:07:50 +02:00
msg := &QuitCommand{}
2012-12-09 21:51:50 +01:00
if len(args) > 0 {
msg.message = args[0]
}
return msg, nil
}
2012-12-10 05:24:53 +01:00
// JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"
type JoinCommand struct {
BaseCommand
channels map[string]string
zero bool
}
func (cmd *JoinCommand) String() string {
return fmt.Sprintf("JOIN(channels=%s, zero=%t)", cmd.channels, cmd.zero)
}
func NewJoinCommand(args []string) (editableCommand, error) {
msg := &JoinCommand{
2013-06-03 07:07:50 +02:00
channels: make(map[string]string),
}
if len(args) == 0 {
return nil, NotEnoughArgsError
2012-12-13 08:27:17 +01:00
}
2012-12-09 21:51:50 +01:00
if args[0] == "0" {
msg.zero = true
return msg, nil
}
channels := strings.Split(args[0], ",")
keys := make([]string, len(channels))
if len(args) > 1 {
for i, key := range strings.Split(args[1], ",") {
keys[i] = key
2012-12-09 21:51:50 +01:00
}
}
for i, channel := range channels {
msg.channels[channel] = keys[i]
}
2012-12-09 21:51:50 +01:00
return msg, nil
}
// PART <channel> *( "," <channel> ) [ <Part Command> ]
type PartCommand struct {
BaseCommand
channels []string
message string
}
2014-02-09 02:53:06 +01:00
func (cmd *PartCommand) Message() string {
if cmd.message == "" {
return cmd.Source().Nick()
}
return cmd.message
}
func (cmd *PartCommand) String() string {
return fmt.Sprintf("PART(channels=%s, message=%s)", cmd.channels, cmd.message)
}
func NewPartCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) < 1 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
msg := &PartCommand{
2013-06-03 07:07:50 +02:00
channels: strings.Split(args[0], ","),
2012-12-13 08:27:17 +01:00
}
2012-12-09 21:51:50 +01:00
if len(args) > 1 {
msg.message = args[1]
}
return msg, nil
}
2012-12-13 08:27:17 +01:00
// PRIVMSG <target> <message>
type PrivMsgCommand struct {
BaseCommand
target string
message string
}
func (cmd *PrivMsgCommand) String() string {
2013-05-11 22:55:01 +02:00
return fmt.Sprintf("PRIVMSG(target=%s, message=%s)", cmd.target, cmd.message)
}
func NewPrivMsgCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) < 2 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
return &PrivMsgCommand{
2013-06-03 07:07:50 +02:00
target: args[0],
message: args[1],
2012-12-09 21:51:50 +01:00
}, nil
}
2012-12-09 23:59:28 +01:00
// TOPIC [newtopic]
type TopicCommand struct {
BaseCommand
channel string
setTopic bool
topic string
}
func (cmd *TopicCommand) String() string {
return fmt.Sprintf("TOPIC(channel=%s, topic=%s)", cmd.channel, cmd.topic)
}
func NewTopicCommand(args []string) (editableCommand, error) {
2012-12-09 21:51:50 +01:00
if len(args) < 1 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
2012-12-09 21:51:50 +01:00
}
msg := &TopicCommand{
2013-06-03 07:07:50 +02:00
channel: args[0],
2012-12-13 08:27:17 +01:00
}
2012-12-09 21:51:50 +01:00
if len(args) > 1 {
msg.setTopic = true
2012-12-09 21:51:50 +01:00
msg.topic = args[1]
}
return msg, nil
}
type ModeChange struct {
mode UserMode
2014-02-09 07:42:14 +01:00
op ModeOp
}
func (change *ModeChange) String() string {
2014-02-09 07:42:14 +01:00
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 {
BaseCommand
nickname string
changes []ModeChange
2012-12-10 05:24:53 +01:00
}
2014-02-09 07:42:14 +01:00
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
func NewUserModeCommand(args []string) (editableCommand, error) {
cmd := &ModeCommand{
nickname: args[0],
changes: make([]ModeChange, 0),
}
2014-02-09 07:42:14 +01:00
for _, modeChange := range args[1:] {
op := ModeOp(modeChange[0])
if (op != Add) && (op != Remove) {
return nil, ErrParseCommand
}
2014-02-09 07:42:14 +01:00
for _, mode := range modeChange[1:] {
cmd.changes = append(cmd.changes, ModeChange{
mode: UserMode(mode),
op: op,
})
}
}
return cmd, nil
}
func (cmd *ModeCommand) String() string {
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
2013-05-11 22:55:01 +02:00
}
type ChannelModeChange struct {
mode ChannelMode
op ModeOp
arg string
}
2014-02-17 20:46:40 +01:00
func (change *ChannelModeChange) String() (str string) {
if (change.op == Add) || (change.op == Remove) {
str = change.op.String()
}
str += change.mode.String()
if change.arg != "" {
str += " " + change.arg
}
return
}
type ChannelModeChanges []ChannelModeChange
2014-02-17 20:46:40 +01:00
func (changes ChannelModeChanges) String() (str string) {
if len(changes) == 0 {
2014-02-17 20:46:40 +01:00
return
}
2014-02-17 20:46:40 +01:00
str = "+"
if changes[0].op == Remove {
str = "-"
}
for _, change := range changes {
str += change.mode.String()
}
for _, change := range changes {
2014-02-17 20:46:40 +01:00
if change.arg == "" {
continue
}
str += " " + change.arg
}
2014-02-17 20:46:40 +01:00
return
}
2014-02-09 03:14:39 +01:00
type ChannelModeCommand struct {
BaseCommand
channel string
changes ChannelModeChanges
2014-02-09 03:14:39 +01:00
}
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
func NewChannelModeCommand(args []string) (editableCommand, error) {
2014-02-09 03:14:39 +01:00
cmd := &ChannelModeCommand{
channel: args[0],
changes: make(ChannelModeChanges, 0),
}
args = args[1:]
for len(args) > 0 {
modeArg := args[0]
2014-02-09 07:42:14 +01:00
op := ModeOp(modeArg[0])
if (op == Add) || (op == Remove) {
modeArg = modeArg[1:]
2014-02-09 07:42:14 +01:00
} else {
op = List
}
2014-02-09 07:42:14 +01:00
skipArgs := 1
2014-02-09 07:42:14 +01:00
for _, mode := range modeArg {
change := ChannelModeChange{
mode: ChannelMode(mode),
op: op,
}
switch change.mode {
2014-02-17 20:46:40 +01:00
case Key, BanMask, ExceptionMask, InviteMask, UserLimit,
ChannelOperator, ChannelCreator, Voice:
if len(args) > skipArgs {
change.arg = args[skipArgs]
skipArgs += 1
}
}
cmd.changes = append(cmd.changes, change)
}
args = args[skipArgs:]
2012-12-10 05:24:53 +01:00
}
2014-02-09 03:14:39 +01:00
return cmd, nil
}
func (msg *ChannelModeCommand) String() string {
return fmt.Sprintf("MODE(channel=%s, changes=%s)", msg.channel, msg.changes)
}
func NewModeCommand(args []string) (editableCommand, error) {
2014-02-09 03:14:39 +01:00
if len(args) == 0 {
return nil, NotEnoughArgsError
}
if IsChannel(args[0]) {
return NewChannelModeCommand(args)
} else {
return NewUserModeCommand(args)
}
}
2014-02-09 02:43:59 +01:00
type WhoisCommand struct {
BaseCommand
target string
masks []string
}
2014-02-09 03:49:52 +01:00
// WHOIS [ <target> ] <mask> *( "," <mask> )
func NewWhoisCommand(args []string) (editableCommand, error) {
2014-02-09 02:43:59 +01:00
if len(args) < 1 {
return nil, NotEnoughArgsError
}
var masks string
var target string
if len(args) > 1 {
target = args[0]
masks = args[1]
} else {
masks = args[0]
}
return &WhoisCommand{
target: target,
masks: strings.Split(masks, ","),
}, nil
}
2014-02-09 03:49:52 +01:00
func (msg *WhoisCommand) String() string {
return fmt.Sprintf("WHOIS(target=%s, masks=%s)", msg.target, msg.masks)
}
2014-02-09 03:49:52 +01:00
type WhoCommand struct {
BaseCommand
2014-02-09 07:42:14 +01:00
mask Mask
2014-02-09 03:49:52 +01:00
operatorOnly bool
}
// WHO [ <mask> [ "o" ] ]
func NewWhoCommand(args []string) (editableCommand, error) {
2014-02-09 03:49:52 +01:00
cmd := &WhoCommand{}
if len(args) > 0 {
2014-02-09 07:42:14 +01:00
cmd.mask = Mask(args[0])
2014-02-09 03:49:52 +01:00
}
if (len(args) > 1) && (args[1] == "o") {
cmd.operatorOnly = true
}
return cmd, nil
}
func (msg *WhoCommand) String() string {
return fmt.Sprintf("WHO(mask=%s, operatorOnly=%s)", msg.mask, msg.operatorOnly)
}
2014-02-09 19:07:40 +01:00
type OperCommand struct {
BaseCommand
name string
password string
}
func (msg *OperCommand) String() string {
return fmt.Sprintf("OPER(name=%s, password=%s)", msg.name, msg.password)
}
// OPER <name> <password>
func NewOperCommand(args []string) (editableCommand, error) {
if len(args) < 2 {
return nil, NotEnoughArgsError
}
return &OperCommand{
name: args[0],
password: args[1],
}, nil
}
2014-02-10 20:14:34 +01:00
// TODO
type CapCommand struct {
BaseCommand
args []string
}
func (msg *CapCommand) String() string {
return fmt.Sprintf("CAP(args=%s)", msg.args)
}
func NewCapCommand(args []string) (editableCommand, error) {
return &CapCommand{
args: args,
}, nil
}
2014-02-11 03:40:06 +01:00
// HAPROXY support
type ProxyCommand struct {
BaseCommand
net string
sourceIP string
destIP string
sourcePort string
destPort string
}
func (msg *ProxyCommand) String() string {
return fmt.Sprintf("PROXY(sourceIP=%s, sourcePort=%s)", msg.sourceIP, msg.sourcePort)
}
func NewProxyCommand(args []string) (editableCommand, error) {
if len(args) < 5 {
return nil, NotEnoughArgsError
}
return &ProxyCommand{
net: args[0],
sourceIP: args[1],
destIP: args[2],
sourcePort: args[3],
destPort: args[4],
}, nil
}
2014-02-12 00:44:58 +01:00
type AwayCommand struct {
BaseCommand
text string
away bool
}
func (msg *AwayCommand) String() string {
return fmt.Sprintf("AWAY(%s)", msg.text)
}
func NewAwayCommand(args []string) (editableCommand, error) {
cmd := &AwayCommand{}
if len(args) > 0 {
cmd.text = args[0]
cmd.away = true
}
return cmd, nil
}
2014-02-12 00:58:54 +01:00
type IsOnCommand struct {
BaseCommand
nicks []string
}
func (msg *IsOnCommand) String() string {
return fmt.Sprintf("ISON(nicks=%s)", msg.nicks)
}
func NewIsOnCommand(args []string) (editableCommand, error) {
if len(args) == 0 {
return nil, NotEnoughArgsError
}
return &IsOnCommand{
nicks: args,
}, nil
}
2014-02-12 01:35:32 +01:00
type MOTDCommand struct {
BaseCommand
target string
}
func NewMOTDCommand(args []string) (editableCommand, error) {
cmd := &MOTDCommand{}
if len(args) > 0 {
cmd.target = args[0]
}
return cmd, nil
}
2014-02-12 02:11:59 +01:00
type NoticeCommand struct {
BaseCommand
target string
message string
}
func (cmd *NoticeCommand) String() string {
return fmt.Sprintf("NOTICE(target=%s, message=%s)", cmd.target, cmd.message)
}
func NewNoticeCommand(args []string) (editableCommand, error) {
if len(args) < 2 {
return nil, NotEnoughArgsError
}
return &NoticeCommand{
target: args[0],
message: args[1],
}, nil
}
2014-02-17 08:29:11 +01:00
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
}
2014-02-17 08:51:27 +01:00
type ListCommand struct {
BaseCommand
channels []string
target string
}
func NewListCommand(args []string) (editableCommand, error) {
cmd := &ListCommand{}
if len(args) > 0 {
cmd.channels = strings.Split(args[0], ",")
}
if len(args) > 1 {
cmd.target = args[1]
}
return cmd, nil
}