package irc import ( "bufio" "fmt" "log" "net" "strconv" "strings" "time" ) //Connection holds the callbacks for the client type Connection struct { Sock net.Conn PrivmsgCallback func(string, string, string) JoinCallback func(string, string) QuitCallback func(string, string) PartCallback func(string, string, string) NickCallback func(string, string) NumericCallback func(string, int, string) joined bool channels []string curNick string userList map[string][]string } //SendPong replies to the received PING func (c *Connection) SendPong(ping string) { pong := strings.Replace(ping, "PING", "PONG", 1) c.Sock.Write([]byte(fmt.Sprintf("%s\n", pong))) log.Println("pong sent") } //SendPrivmsg sends a privmsg to channel/target func (c *Connection) SendPrivmsg(channel, msg string) { _, err := c.Sock.Write([]byte(fmt.Sprintf("PRIVMSG %s :%s\n", channel, msg))) if err != nil { log.Printf("Error sending private message: %s\n", err.Error()) } } //SendNotice sends a notice to channel/target func (c *Connection) SendNotice(channel, msg string) { _, err := c.Sock.Write([]byte(fmt.Sprintf("NOTICE %s :%s\n", channel, msg))) if err != nil { log.Printf("Error sending NOTICE message: %s\n", err.Error()) } } //SendNick changes the clients nick func (c *Connection) SendNick(nick string) { c.Sock.Write([]byte(fmt.Sprintf("NICK %s\n", nick))) } //SendJoin sends a JOIN func (c *Connection) SendJoin(channel string) { c.Sock.Write([]byte(fmt.Sprintf("JOIN %s\n", channel))) } //SendPart sends a PART func (c *Connection) SendPart(channel, msg string) { c.Sock.Write([]byte(fmt.Sprintf("PART %s :%s\n", channel, msg))) } //SendQuit sends a QUIT func (c *Connection) SendQuit(msg string) { c.Sock.Write([]byte(fmt.Sprintf("QUIT :%s\n", msg))) } //SendNames sends a NAMES request for the channel func (c *Connection) SendNames(channel string) { c.Sock.Write([]byte(fmt.Sprintf("NAMES %s\n", channel))) } //DelayedSend sends a privmsg AFTER the specified delay func (c *Connection) DelayedSend(sock net.Conn, dur time.Duration, channel, msg string) { time.Sleep(dur) c.SendPrivmsg(channel, msg) } //NewConnection creates and connects an Connection func NewConnection(server, nick, user, pass string, chans []string) *Connection { irc := &Connection{channels: chans} sock, err := net.Dial("tcp", server) if err != nil { log.Fatalln(err.Error()) } irc.Sock = sock irc.Sock.Write([]byte(fmt.Sprintf("USER %s * * :%s\n", user, user))) irc.SendNick(nick) irc.curNick = nick irc.Sock.Write([]byte(fmt.Sprintf("PASS %s\n", pass))) return irc } //GetNick gets the nickname portion of a host string func GetNick(name string) string { return strings.Split(name, "!")[0] } //TODO: simplify this to pass gocyclo func (c *Connection) parseMessage(line string) { if line[0] == ':' { buf := line[1:] params := strings.SplitN(buf, " ", 3) from := params[0] cmd := params[1] args := params[2] if _, e := strconv.Atoi(cmd); e == nil { //numeric message if !c.joined { if cmd == "376" { for _, v := range c.channels { c.SendJoin(v) } c.joined = true } } if cmd == "353" { idx := strings.Index(args, "=") idx += 2 s := strings.Split(args[idx:], ":") log.Printf("names: %s: %s\n", s[0], s[1]) } if c.NumericCallback != nil { code, _ := strconv.Atoi(cmd) c.NumericCallback(from, code, args) } } else { t := strings.SplitN(args, ":", 2) target := strings.TrimSpace(t[0]) msg := "" if len(t) > 1 { msg = t[1] } switch strings.ToLower(cmd) { case "privmsg": if c.PrivmsgCallback != nil { c.PrivmsgCallback(target, from, msg) } case "join": if c.JoinCallback != nil { c.JoinCallback(from, target) } case "quit": if c.QuitCallback != nil { c.QuitCallback(from, msg) } case "part": if c.PartCallback != nil { c.PartCallback(from, target, msg) } case "nick": if GetNick(from) == c.curNick { c.curNick = msg log.Printf("BOT NICK CHANGED TO: %s\n", c.curNick) } if c.NickCallback != nil { c.NickCallback(from, msg) } default: log.Printf("unhandled command: %s %s %s", cmd, target, msg) } } } } //Run runs the irc event loop func (c *Connection) Run() { rdr := bufio.NewScanner(c.Sock) c.joined = false for rdr.Scan() { line := rdr.Text() if strings.HasPrefix(line, "PING") { c.SendPong(line) continue } c.parseMessage(line) } }