ergo/irc/commands.go

454 lines
8.4 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"
"unicode/utf8"
2012-12-09 21:51:50 +01:00
)
2013-05-09 20:05:10 +02:00
type Command interface {
Client() *Client
Source() Identifier
2013-06-03 07:07:50 +02:00
Reply(Reply)
2013-05-09 20:05:10 +02:00
HandleServer(*Server)
2012-12-13 08:27:17 +01:00
}
type EditableCommand interface {
Command
2013-06-03 07:07:50 +02:00
SetBase(*Client)
}
var (
2013-05-09 20:05:10 +02:00
NotEnoughArgsError = errors.New("not enough arguments")
ErrParseCommand = errors.New("failed to parse message")
parseCommandFuncs = map[string]func([]string) (EditableCommand, error){
"JOIN": NewJoinCommand,
"MODE": NewModeCommand,
"NICK": NewNickCommand,
"PART": NewPartCommand,
"PASS": NewPassCommand,
"PING": NewPingCommand,
"PONG": NewPongCommand,
"PRIVMSG": NewPrivMsgCommand,
"QUIT": NewQuitCommand,
"TOPIC": NewTopicCommand,
"USER": NewUserMsgCommand,
}
)
2013-05-09 20:05:10 +02:00
type BaseCommand struct {
client *Client
}
func (command *BaseCommand) Client() *Client {
2013-05-11 22:55:01 +02:00
return command.client
2013-05-09 20:05:10 +02:00
}
2013-06-03 07:07:50 +02:00
func (command *BaseCommand) SetBase(c *Client) {
*command = BaseCommand{c}
2013-05-09 20:05:10 +02:00
}
func (command *BaseCommand) Source() Identifier {
2013-06-03 07:07:50 +02:00
return command.client
}
func (command *BaseCommand) Reply(reply Reply) {
command.client.Replies() <- reply
}
func ParseCommand(line string) (EditableCommand, error) {
command, args := parseLine(line)
constructor := parseCommandFuncs[command]
if constructor == nil {
return NewUnknownCommand(command, args), nil
}
return constructor(args)
}
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
}
func parseLine(line string) (command string, args []string) {
args = make([]string, 0)
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
args = append(args, arg)
}
command, args = strings.ToUpper(args[0]), args[1:]
return
}
// <command> [args...]
type UnknownCommand struct {
BaseCommand
command string
args []string
}
func (cmd *UnknownCommand) String() string {
return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.command, cmd.args)
}
func NewUnknownCommand(command string, args []string) *UnknownCommand {
return &UnknownCommand{
2013-06-03 07:07:50 +02:00
command: command,
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
}
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
}
func (m *PrivMsgCommand) TargetIsChannel() bool {
switch m.target[0] {
case '&', '#', '+', '!':
return true
}
return false
}
2012-12-09 23:59:28 +01:00
// TOPIC [newtopic]
type TopicCommand struct {
BaseCommand
channel string
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.topic = args[1]
}
return msg, nil
}
type Mode rune
const (
Away Mode = 'a'
Invisible Mode = 'i'
WallOps Mode = 'w'
Restricted Mode = 'r'
Operator Mode = 'o'
LocalOperator Mode = 'O'
ServerNotice Mode = 's'
)
type ModeChange struct {
mode Mode
add bool // false => remove
}
func (change *ModeChange) String() string {
sig := "+"
if !change.add {
sig = "-"
}
return fmt.Sprintf("%s%s", sig, change.mode)
}
type ModeCommand struct {
BaseCommand
nickname string
changes []ModeChange
2012-12-10 05:24:53 +01:00
}
func (cmd *ModeCommand) String() string {
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
}
func stringToRunes(str string) <-chan rune {
runes := make(chan rune)
go func() {
for len(str) > 0 {
rune, size := utf8.DecodeRuneInString(str)
runes <- rune
str = str[size:]
}
close(runes)
}()
return runes
2013-05-11 22:55:01 +02:00
}
func NewModeCommand(args []string) (EditableCommand, error) {
if len(args) == 0 {
2012-12-10 05:24:53 +01:00
return nil, NotEnoughArgsError
}
cmd := &ModeCommand{
2013-06-03 07:07:50 +02:00
nickname: args[0],
changes: make([]ModeChange,
utf8.RuneCountInString(strings.Join(args[1:], ""))-len(args[1:])),
}
index := 0
for _, arg := range args[1:] {
modeChange := stringToRunes(arg)
sig := <-modeChange
if sig != '+' && sig != '-' {
return nil, ErrParseCommand
}
add := sig == '+'
for mode := range modeChange {
cmd.changes[index] = ModeChange{
mode: Mode(mode),
add: add,
}
index += 1
}
}
return cmd, nil
2012-12-10 05:24:53 +01:00
}