dispatch/server/irc.go

305 lines
5.2 KiB
Go
Raw Normal View History

package server
2015-01-17 01:37:21 +00:00
import (
"bufio"
"crypto/tls"
"fmt"
2015-02-22 07:57:52 +00:00
"log"
2015-01-17 01:37:21 +00:00
"net"
"strings"
"sync"
"time"
2015-01-17 01:37:21 +00:00
)
const (
PING = "PING"
NICK = "NICK"
2015-01-17 01:37:21 +00:00
JOIN = "JOIN"
PART = "PART"
MODE = "MODE"
PRIVMSG = "PRIVMSG"
NOTICE = "NOTICE"
TOPIC = "TOPIC"
QUIT = "QUIT"
RPL_WELCOME = "001"
RPL_YOURHOST = "002"
RPL_CREATED = "003"
RPL_LUSERCLIENT = "251"
RPL_LUSEROP = "252"
RPL_LUSERUNKNOWN = "253"
RPL_LUSERCHANNELS = "254"
RPL_LUSERME = "255"
2015-02-21 12:06:05 +00:00
RPL_AWAY = "301"
2015-01-17 01:37:21 +00:00
RPL_WHOISUSER = "311"
RPL_WHOISSERVER = "312"
RPL_WHOISOPERATOR = "313"
RPL_WHOISIDLE = "317"
RPL_ENDOFWHOIS = "318"
RPL_WHOISCHANNELS = "319"
RPL_TOPIC = "332"
RPL_NAMREPLY = "353"
RPL_ENDOFNAMES = "366"
RPL_MOTD = "372"
RPL_MOTDSTART = "375"
RPL_ENDOFMOTD = "376"
)
type Message struct {
Prefix string
Command string
Params []string
Trailing string
}
type IRC struct {
conn net.Conn
reader *bufio.Reader
out chan string
ready sync.WaitGroup
once sync.Once
nick string
nickLock sync.Mutex
2015-01-17 01:37:21 +00:00
Messages chan *Message
Server string
Host string
TLS bool
TLSConfig *tls.Config
Password string
2015-01-17 01:37:21 +00:00
Username string
Realname string
}
func NewIRC(nick, username string) *IRC {
return &IRC{
nick: nick,
Username: username,
Realname: nick,
Messages: make(chan *Message, 32),
out: make(chan string, 32),
2015-01-17 01:37:21 +00:00
}
}
func (i *IRC) Connect(address string) error {
2015-01-17 01:37:21 +00:00
if idx := strings.Index(address, ":"); idx < 0 {
i.Host = address
if i.TLS {
address += ":6697"
} else {
address += ":6667"
}
} else {
i.Host = address[:idx]
}
i.Server = address
dialer := &net.Dialer{Timeout: 30 * time.Second}
2015-01-17 01:37:21 +00:00
if i.TLS {
if i.TLSConfig == nil {
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
if conn, err := tls.DialWithDialer(dialer, "tcp", address, i.TLSConfig); err != nil {
return err
} else {
i.conn = conn
}
2015-01-17 01:37:21 +00:00
} else {
if conn, err := dialer.Dial("tcp", address); err != nil {
return err
} else {
i.conn = conn
}
2015-01-17 01:37:21 +00:00
}
i.reader = bufio.NewReader(i.conn)
if i.Password != "" {
i.Pass(i.Password)
}
2015-01-17 01:37:21 +00:00
i.Nick(i.nick)
i.User(i.Username, i.Realname)
i.ready.Add(1)
go i.send()
2015-01-17 01:37:21 +00:00
go i.recv()
return nil
2015-01-17 01:37:21 +00:00
}
func (i *IRC) Pass(password string) {
i.write("PASS " + password)
2015-01-17 01:37:21 +00:00
}
func (i *IRC) Nick(nick string) {
i.write("NICK " + nick)
i.nickLock.Lock()
i.nick = nick
i.nickLock.Unlock()
2015-01-17 01:37:21 +00:00
}
func (i *IRC) User(username, realname string) {
i.writef("USER %s 0 * :%s", username, realname)
2015-01-17 01:37:21 +00:00
}
func (i *IRC) Oper(name, password string) {
i.Write("OPER " + name + " " + password)
}
func (i *IRC) Mode(target, modes, params string) {
i.Write(strings.TrimRight("MODE "+target+" "+modes+" "+params, " "))
}
func (i *IRC) Quit() {
go func() {
i.ready.Wait()
i.write("QUIT")
i.conn.Close()
}()
}
2015-01-17 01:37:21 +00:00
func (i *IRC) Join(channels ...string) {
i.Write("JOIN " + strings.Join(channels, ","))
}
func (i *IRC) Part(channels ...string) {
i.Write("PART " + strings.Join(channels, ","))
}
func (i *IRC) Topic(channel string) {
i.Write("TOPIC " + channel)
}
func (i *IRC) Invite(nick, channel string) {
i.Write("INVITE " + nick + " " + channel)
}
func (i *IRC) Kick(channel string, users ...string) {
i.Write("KICK " + channel + " " + strings.Join(users, ","))
}
2015-01-17 01:37:21 +00:00
func (i *IRC) Privmsg(target, msg string) {
i.Writef("PRIVMSG %s :%s", target, msg)
}
func (i *IRC) Notice(target, msg string) {
i.Writef("NOTICE %s :%s", target, msg)
}
func (i *IRC) Whois(nick string) {
i.Write("WHOIS " + nick)
}
2015-02-21 12:06:05 +00:00
func (i *IRC) Away(message string) {
i.Write("AWAY :" + message)
}
func (i *IRC) GetNick() string {
i.nickLock.Lock()
defer i.nickLock.Unlock()
return i.nick
}
2015-01-17 01:37:21 +00:00
func (i *IRC) Write(data string) {
i.out <- data + "\r\n"
2015-01-17 01:37:21 +00:00
}
func (i *IRC) Writef(format string, a ...interface{}) {
i.out <- fmt.Sprintf(format+"\r\n", a...)
}
func (i *IRC) write(data string) {
i.conn.Write([]byte(data + "\r\n"))
}
func (i *IRC) writef(format string, a ...interface{}) {
2015-01-17 01:37:21 +00:00
fmt.Fprintf(i.conn, format+"\r\n", a...)
}
func (i *IRC) send() {
i.ready.Wait()
for {
_, err := i.conn.Write([]byte(<-i.out))
if err != nil {
return
}
}
}
2015-01-17 01:37:21 +00:00
func (i *IRC) recv() {
defer i.close()
2015-01-17 01:37:21 +00:00
for {
line, err := i.reader.ReadString('\n')
if err != nil {
2015-02-22 07:57:52 +00:00
log.Println("IRC connection to", i.Server, "died")
2015-01-17 01:37:21 +00:00
return
}
msg := parseMessage(line)
msg.Prefix = parseUser(msg.Prefix)
i.Messages <- msg
2015-01-17 01:37:21 +00:00
switch msg.Command {
case PING:
go i.write("PONG :" + msg.Trailing)
case RPL_WELCOME:
i.once.Do(i.ready.Done)
2015-01-17 01:37:21 +00:00
}
}
}
func (i *IRC) close() {
i.conn.Close()
i.once.Do(i.ready.Done)
close(i.out)
close(i.Messages)
}
2015-01-17 01:37:21 +00:00
func parseMessage(line string) *Message {
line = strings.Trim(line, "\r\n")
msg := Message{}
cmdStart := 0
cmdEnd := len(line)
if strings.HasPrefix(line, ":") {
cmdStart = strings.Index(line, " ") + 1
msg.Prefix = line[1 : cmdStart-1]
}
if i := strings.Index(line, " :"); i > 0 {
2015-01-17 01:37:21 +00:00
cmdEnd = i
msg.Trailing = line[i+2:]
}
cmd := strings.Split(line[cmdStart:cmdEnd], " ")
msg.Command = cmd[0]
if len(cmd) > 1 {
msg.Params = cmd[1:]
}
if msg.Trailing != "" {
msg.Params = append(msg.Params, msg.Trailing)
}
2015-01-17 01:37:21 +00:00
return &msg
}
func parseUser(user string) string {
if i := strings.Index(user, "!"); i > 0 {
return user[:i]
}
return user
}