package main import ( "crypto/tls" "flag" "fmt" "log" "strings" irc "github.com/fluffle/goirc/client" lru "github.com/hashicorp/golang-lru" ) const maxLen = 450 type notify struct { conn *irc.Conn target string } func newNotify(conn *irc.Conn, target string) *notify { return ¬ify{conn: conn, target: target} } func limit(n int, s string) string { r := []rune(s) if sz := len(r); sz > n { r = append(r[:n-3], []rune("...")...) } return string(r) } func (n *notify) Write(p []byte) (int, error) { s := limit(maxLen, string(p)) log.Println("send", s) n.conn.Notice(n.target, s) return len(p), nil } func privmsg(room string) irc.HandlerFunc { var ( last, _ = lru.New(100) titles, _ = lru.New(100) ) return func(conn *irc.Conn, line *irc.Line) { defer func() { if r := recover(); r != nil { log.Println("panic", r) } }() t := line.Text() l, okLast := last.Get(line.Nick) switch { case line.Nick == conn.Me().Nick: // ignore self case isFlood(t): log.Println("flood", line.Nick) conn.Kick(room, line.Nick) case okLast && noSpaceCompare(t, l.(string)): log.Println("kick", line.Nick) conn.Kick(room, line.Nick) case strings.HasPrefix(t, "s"): global := strings.HasSuffix(t, "g") if tofix, ok := last.Get(line.Nick); ok { fixed, err := re(tofix.(string), t[1:], global) if err == nil && fixed != tofix { log.Println("regexp", t) fmt.Fprintf(newNotify(conn, line.Target()), "%v meant to say: %s", line.Nick, fixed) return } } fallthrough default: for _, v := range getLinks(t) { title, ok := titles.Get(v) if !ok { var err error title, err = getTitle(v) if err != nil { log.Println(v, err) } titles.Add(v, title) } if title != "" { w := newNotify(conn, line.Target()) if ok { fmt.Fprintf(w, "Title: %v (cached)", title) } else { fmt.Fprintf(w, "Title: %v", title) } } } last.Add(line.Nick, t) } } } func main() { var ( node = flag.String("node", "irc.freenode.org", "IRC server") notls = flag.Bool("notls", false, "disable TLS") room = flag.String("room", "#lor", "IRC channel") name = flag.String("name", "dim13", "bots name") ) flag.Parse() conf := irc.NewConfig(*name) conf.Server = *node if !*notls { conf.SSL = true conf.SSLConfig = &tls.Config{ServerName: *node} } conn := irc.Client(conf) done := make(chan struct{}) conn.HandleFunc(irc.DISCONNECTED, func(_ *irc.Conn, _ *irc.Line) { close(done) }) conn.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, _ *irc.Line) { conn.Join(*room) watch(newNotify(conn, *room), feeds) }) conn.HandleBG(irc.PRIVMSG, privmsg(*room)) conn.HandleFunc(irc.KICK, func(conn *irc.Conn, _ *irc.Line) { conn.Join(*room) }) if err := conn.Connect(); err != nil { log.Fatal(err) } <-done }