From 3ef3cf3b0b355eb4206c4b292fce8729df9db5b7 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Sat, 19 Jul 2014 00:09:59 +0000 Subject: [PATCH] Welp. Primitive IRC client in go. --- irc.go | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 irc.go diff --git a/irc.go b/irc.go new file mode 100644 index 0000000..b3ec4b0 --- /dev/null +++ b/irc.go @@ -0,0 +1,176 @@ +package main + +import ( + "bufio" + "crypto/tls" + "flag" + "fmt" + "log" + "net" + "os" + "strings" +) + +type Message struct { + Command string + FullSender string + Sender string + Forum string + Args []string + Text string +} + +func (m Message) String() string { + a := append([]string{m.FullSender}, m.Args...) + args :=strings.Join(a, " ") + return fmt.Sprintf("%s %s %s %s %s", m.Command, m.Sender, m.Forum, args, m.Text) +} + +func nuhost(s string) (string, string, string) { + var parts []string + + parts = strings.SplitN(s, "!", 2) + if len(parts) == 1 { + return s, "", "" + } + n := parts[0] + parts = strings.SplitN(parts[1], "@", 2) + if len(parts) == 1 { + return s, "", "" + } + return n, parts[0], parts[1] +} + +func connect(host string, dotls bool) (net.Conn, error) { + if dotls { + config := &tls.Config{ + InsecureSkipVerify: true, + } + return tls.Dial("tcp", host, config) + } else { + return net.Dial("tcp", host) + } +} + +func readLoop(conn net.Conn, inq chan<- string) { + scanner := bufio.NewScanner(conn) + for scanner.Scan() { + inq <- scanner.Text() + } + close(inq) +} + +func writeLoop(conn net.Conn, outq <-chan string) { + for v := range outq { + fmt.Println(v) + fmt.Fprintln(conn, v) + } +} + +func parse(v string) (Message, error) { + var m Message + var parts []string + var lhs string + + fmt.Println(v) + parts = strings.SplitN(v, " :", 2) + if len(parts) == 2 { + lhs = parts[0] + m.Text = parts[1] + } else { + lhs = v + m.Text = "" + } + + m.FullSender = "." + m.Forum = "." + m.Sender = "." + + parts = strings.Split(lhs, " ") + if parts[0][0] == ':' { + m.FullSender = parts[0][1:] + parts = parts[1:] + + n, u, _ := nuhost(m.FullSender) + if u != "" { + m.Sender = n + } + } + + m.Command = strings.ToUpper(parts[0]) + switch (m.Command) { + case "PRIVMSG", "NOTICE": + n, u, _ := nuhost(parts[1]) + if u == "" { + m.Forum = m.Sender + } else { + m.Forum = n + } + case "PART", "MODE", "TOPIC", "KICK": + m.Forum = parts[1] + case "JOIN": + if len(parts) == 1 { + m.Forum = m.Text + m.Text = "" + } else { + m.Forum = parts[1] + } + case "INVITE": + if m.Text != "" { + m.Forum = m.Text + m.Text = "" + } else { + m.Forum = parts[2] + } + case "NICK": + m.FullSender = parts[1] + m.Forum = m.FullSender + } + + return m, nil +} + +func dispatch(outq chan<- string, m Message) { + log.Print(m.String()) + switch (m.Command) { + case "PING": + outq <- "PONG :" + m.Text + } +} + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] HOST:PORT\n", os.Args[0]) + flag.PrintDefaults() +} + +func main() { + dotls := flag.Bool("notls", true, "Disable TLS security") + + flag.Parse() + if flag.NArg() != 1 { + fmt.Fprintln(os.Stderr, "Error: must specify host") + os.Exit(69) + } + + conn, err := connect(flag.Arg(0), *dotls) + if err != nil { + log.Fatal(err) + } + + inq := make(chan string) + outq := make(chan string) + go readLoop(conn, inq) + go writeLoop(conn, outq) + + outq <- "NICK neale" + outq <- "USER neale neale neale :neale" + for v := range inq { + p, err := parse(v) + if err != nil { + continue + } + dispatch(outq, p) + } + + close(outq) +}