kunt

golang IRC bot
git clone git://git.2f30.org/kunt.git
Log | Files | Refs | LICENSE

commit 14522421484a391eaf2fc90489367968603956ca
parent d8835f83c84876fef279f8d7055e3f1ae8e93e37
Author: sin <sin@2f30.org>
Date:   Wed Apr 17 12:05:56 +0100

kunt: Update kunt to use the irc package

Diffstat:
src/kunt/kunt.go | 286++++++++++++++++++++++++-------------------------------------------------------
1 file changed, 87 insertions(+), 199 deletions(-)
diff --git a/src/kunt/kunt.go b/src/kunt/kunt.go @@ -2,43 +2,22 @@ package main import ( "bytes" - "crypto/tls" - "crypto/x509" "flag" "fmt" "fsdb" "io/ioutil" + "irc" "log" "math/rand" - "net" "os" "os/exec" "strings" "time" ) -/* Kunt context */ type kuntCtx struct { - channel string - nick string - user string - srv string - port string - ssl bool stime time.Time -} - -func newKunt(channel string, nick string, - user string, srv string, port string, ssl bool) *kuntCtx { - return &kuntCtx{channel, nick, user, srv, port, ssl, time.Now()} -} - -/* Helper routines */ -func botWrite(conn net.Conn, buf []byte) { - _, err := conn.Write(buf[0:]) - if err != nil { - log.Fatal(err) - } + ircCtx *irc.IrcContext } func resolveYoutubeTitle(link string) (title string, ret bool) { @@ -58,8 +37,7 @@ func resolveYoutubeTitle(link string) (title string, ret bool) { return } -/* Kunt command handling routines */ -func cmdKunt(conn net.Conn, s string) { +func cmdKunt(msg irc.IrcMessage) { actions := []string{ "die", "die bitch", @@ -71,11 +49,10 @@ func cmdKunt(conn net.Conn, s string) { "no fucking way", } idx := rand.Intn(len(actions)) - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, actions[idx]) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], string(actions[idx])) } -func cmdWisdom(conn net.Conn, s string) { +func cmdWisdom(msg irc.IrcMessage) { out, err := exec.Command("./fortune").Output() if err != nil { log.Fatal(err) @@ -85,13 +62,12 @@ func cmdWisdom(conn net.Conn, s string) { if i == "" { continue } - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, i) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], i) time.Sleep(512 * time.Millisecond) } } -func cmdHelp(conn net.Conn, s string) { +func cmdHelp(msg irc.IrcMessage) { help := []string{ "!addquote <quote> -> Add a quote to the db", "!randquote -> Print a random quote from the db", @@ -106,49 +82,44 @@ func cmdHelp(conn net.Conn, s string) { "!version -> Show version", } for _, i := range help { - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, i) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], i) time.Sleep(512 * time.Millisecond) } } -func cmdVersion(conn net.Conn, s string) { - ver := "v0.2.7" - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, ver) - botWrite(conn, []byte(r)) +func cmdVersion(msg irc.IrcMessage) { + ver := "v0.2.8" + kunt.ircCtx.SendPrivmsg(msg.Params[0], ver) } -func cmdRandQuote(conn net.Conn, s string) { +func cmdRandQuote(msg irc.IrcMessage) { if quoteDb.Empty() { - r := fmt.Sprintf("PRIVMSG %s :Empty quote database\r\n", - kunt.channel) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], "Empty quote database") return } quote, idx := quoteDb.Rand() - /* Print idx in red */ - r := fmt.Sprintf("PRIVMSG %s :%s%02d[%d]%s %s\r\n", kunt.channel, + // Print idx in red + text := fmt.Sprintf("%s%02d[%d]%s %s\r\n", "\003", '5', idx, "\003", string(quote)) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], text) } -func cmdAddQuote(conn net.Conn, s string) { - idx := len("!addquote") - if s[idx:idx+1] != " " { - r := fmt.Sprintf("PRIVMSG %s :Missing parameter for !addquote\r\n", - kunt.channel) - botWrite(conn, []byte(r)) +func cmdAddQuote(msg irc.IrcMessage) { + if len(msg.Params) != 2 { + kunt.ircCtx.SendPrivmsg(msg.Params[0], "Missing parameter for !addquote\r\n") return } - s = s[idx+1:] - s = strings.TrimSpace(s) - s += "\n" + + s := "" + for i, v := range msg.Params { + if i > 1 { + s += v + } + } buf := []byte(s) quote, err := quoteDb.Put(-1, buf) if err != nil { - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", - kunt.channel, err.Error()) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], err.Error()) return } err = quoteDb.FsWrite(quote, buf) @@ -157,49 +128,40 @@ func cmdAddQuote(conn net.Conn, s string) { } } -func cmdCountQuotes(conn net.Conn, s string) { - r := fmt.Sprintf("PRIVMSG %s :The quote DB has %d quotes\r\n", - kunt.channel, quoteDb.Len()) - botWrite(conn, []byte(r)) +func cmdCountQuotes(msg irc.IrcMessage) { + text := fmt.Sprintf("The quote DB has %d quotes", quoteDb.Len()) + kunt.ircCtx.SendPrivmsg(msg.Params[0], text) } -func cmdRandUrl(conn net.Conn, s string) { +func cmdRandUrl(msg irc.IrcMessage) { if urlDb.Empty() { - r := fmt.Sprintf("PRIVMSG %s :Empty url database\r\n", - kunt.channel) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], "Empty url database") return } url, idx := urlDb.Rand() - /* Print idx in red */ - r := fmt.Sprintf("PRIVMSG %s :%s%02d[%d]%s %s\r\n", kunt.channel, + // Print idx in red + r := fmt.Sprintf("%s%02d[%d]%s %s", "\003", '5', idx, "\003", string(url)) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], r) title, ok := resolveYoutubeTitle(string(url)) if ok { - r = fmt.Sprintf("PRIVMSG %s :[YouTube] %s\r\n", - kunt.channel, title) - botWrite(conn, []byte(r)) + r = fmt.Sprintf("[YouTube] %s", title) + kunt.ircCtx.SendPrivmsg(msg.Params[0], r) } } -func cmdAddUrl(conn net.Conn, s string) { - idx := len("!addurl") - if s[idx:idx+1] != " " { - r := fmt.Sprintf("PRIVMSG %s :Missing parameter for !addurl\r\n", - kunt.channel) - botWrite(conn, []byte(r)) +func cmdAddUrl(msg irc.IrcMessage) { + if len(msg.Params) != 2 { + kunt.ircCtx.SendPrivmsg(msg.Params[0], "Wrong formatting for !addurl") return } - s = s[idx+1:] - s = strings.TrimSpace(s) + + s := strings.TrimSpace(msg.Params[1]) s += "\n" buf := []byte(s) url, err := urlDb.Put(-1, buf) if err != nil { - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", - kunt.channel, err.Error()) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], err.Error()) return } err = urlDb.FsWrite(url, buf) @@ -208,30 +170,26 @@ func cmdAddUrl(conn net.Conn, s string) { } } -func cmdCountUrls(conn net.Conn, s string) { - r := fmt.Sprintf("PRIVMSG %s :The url DB has %d urls\r\n", - kunt.channel, urlDb.Len()) - botWrite(conn, []byte(r)) +func cmdCountUrls(msg irc.IrcMessage) { + r := fmt.Sprintf("The url DB has %d urls", urlDb.Len()) + kunt.ircCtx.SendPrivmsg(msg.Params[0], r) } -func cmdUptime(conn net.Conn, s string) { +func cmdUptime(msg irc.IrcMessage) { etime := time.Now() - r := fmt.Sprintf("PRIVMSG %s :%v\r\n", kunt.channel, - etime.Sub(kunt.stime)) - botWrite(conn, []byte(r)) + r := fmt.Sprintf("%v", etime.Sub(kunt.stime)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], r) } -func cmdSrc(conn net.Conn, s string) { +func cmdSrc(msg irc.IrcMessage) { src := "http://amnezia.2f30.org/tmp/kunt-latest.tgz" - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, src) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], src) } -func cmdTodo(conn net.Conn, s string) { +func cmdTodo(msg irc.IrcMessage) { todo, err := ioutil.ReadFile("TODO") if err != nil { - r := fmt.Sprintf("PRIVMSG %s :No TODO list available\r\n", kunt.channel) - botWrite(conn, []byte(r)) + kunt.ircCtx.SendPrivmsg(msg.Params[0], "No TODO list available") return } sf := strings.FieldsFunc(string(todo), func(r rune) bool { @@ -241,10 +199,8 @@ func cmdTodo(conn net.Conn, s string) { } return false }) - for _, i := range sf { - r := fmt.Sprintf("PRIVMSG %s :%s\r\n", kunt.channel, i) - botWrite(conn, []byte(r)) - time.Sleep(512 * time.Millisecond) + for _, v := range sf { + kunt.ircCtx.SendPrivmsg(msg.Params[0], v) } } @@ -252,81 +208,32 @@ var sslon = flag.Bool("s", false, "SSL support") var quoteDb *fsdb.Fsdb var urlDb *fsdb.Fsdb -var kunt *kuntCtx +var kunt kuntCtx + +func handlePrivmsg(msg irc.IrcMessage) { +} func main() { - flag.Parse() + log.SetPrefix("kunt: ") + flag.Parse() if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, "usage: %s [-s] host:port\n", os.Args[0]) os.Exit(1) } - service := flag.Arg(0) - - log.SetPrefix("kunt: ") hostport := strings.Split(flag.Arg(0), ":") - kunt = newKunt("#2f30", "kunt", "z0mg", hostport[0], hostport[1], *sslon) - - if *sslon { - fmt.Println("SSL on") - } - - tcpAddr, err := net.ResolveTCPAddr("tcp4", service) - if err != nil { - log.Fatal(err) - } - if *sslon { - cert, err := tls.LoadX509KeyPair("certs/client.pem", - "certs/client.key") - if err != nil { - log.Fatal(err) - } - conf := tls.Config{Certificates: []tls.Certificate{cert}, - InsecureSkipVerify: true} - - conn, err := tls.Dial("tcp", service, &conf) - if err != nil { - log.Fatal(err) - } - defer conn.Close() - - fmt.Println("Client: connected to: ", conn.RemoteAddr()) - - state := conn.ConnectionState() - - for _, v := range state.PeerCertificates { - fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey)) - fmt.Println(v.Subject) - } - - fmt.Println("Client: handshake: ", state.HandshakeComplete) - fmt.Println("Client: mutual: ", state.NegotiatedProtocolIsMutual) - loop(conn) - } else { - conn, err := net.DialTCP("tcp", nil, tcpAddr) - if err != nil { - log.Fatal(err) - } - defer conn.Close() - loop(conn) - } -} - -func loop(conn net.Conn) { quoteDb = fsdb.NewFsdb("Quote DB", "db/quotes", "quote") urlDb = fsdb.NewFsdb("Url DB", "db/urls", "url") quoteDb.Load() urlDb.Load() - go InitiateConnection(conn) - rand.Seed(time.Now().UnixNano()) - dispatch := map[string]func(net.Conn, string){ + dispatch := map[string]func(irc.IrcMessage){ "kunt": cmdKunt, "!wisdom": cmdWisdom, "!help": cmdHelp, @@ -342,55 +249,36 @@ func loop(conn net.Conn) { "!TODO": cmdTodo, } - for { - var buf [512]byte - n, err := conn.Read(buf[0:]) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(buf[0 : n-1])) - s := string(buf[0:n]) - for i := range buf { - buf[i] = 0 - } + cfg := irc.IrcConfig{ + irc.Network: "grnet", + irc.Nick: "kunt", + irc.User: "z0mg", + irc.Serv: hostport[0], + irc.Port: hostport[1], + irc.Tls: *sslon, + } - if strings.HasPrefix(s, "PING :") { - r := strings.Replace(s, "PING :", "PONG :", 6) - r = r + "\r\n" - _, err := conn.Write([]byte(r)) - if err != nil { - log.Fatal(err) + kunt.ircCtx = irc.NewIrcContext(cfg) + kunt.ircCtx.AddEventHandler(irc.IrcEvent{"PRIVMSG", func(msg irc.IrcMessage) { + cmd := msg.Params[1] + // Strip the leading ':' + cmd = cmd[1:] + for i, v := range dispatch { + if strings.HasPrefix(cmd, i) { + go v(msg) } - continue } + }}) - q := fmt.Sprintf("PRIVMSG %s :", kunt.channel) - msg := strings.Index(s, q) - if msg == -1 { - continue - } - msg += len(q) - s = s[msg:] - s = strings.TrimSpace(s) - s += "\n" + kunt.ircCtx.Connect() + kunt.ircCtx.AddChannel("#2f30") + kunt.ircCtx.JoinChannels() - for i, v := range dispatch { - if strings.HasPrefix(s, i) { - go v(conn, s) - break - } + donechan := make(chan bool) + select { + case stat := <-donechan: + if stat { + break } } - - os.Exit(0) -} - -func InitiateConnection(conn net.Conn) { - fmt.Println("initcon") - query := fmt.Sprintf("NICK %s\r\n", kunt.nick) - botWrite(conn, []byte(query)) - query = fmt.Sprintf("USER %s * 8 :%s\r\n", kunt.user, kunt.nick) - botWrite(conn, []byte(query)) - query = fmt.Sprintf("JOIN %s\r\n", kunt.channel) - botWrite(conn, []byte(query)) }