func handleAuth(irc *goirc.Connection) { // place a callback on nickserv identification and wait until it is done if cfg.Irc.Nickpass != "" { identwaiter := make(chan bool) irc.AddCallback("NOTICE", func(e *goirc.Event) { re := regexp.MustCompile("NickServ IDENTIFY") if e.Nick == "NickServ" && re.MatchString(e.Message()) { irc.Privmsgf("NickServ", "IDENTIFY %s", cfg.Irc.Nickpass) } reaccepted := regexp.MustCompile("(?i)Password accepted") if e.Nick == "NickServ" && reaccepted.MatchString(e.Message()) { identwaiter <- true } }) for { select { case <-identwaiter: goto identified case <-time.After(5 * time.Second): irc.Privmsgf("NickServ", "IDENTIFY %s", cfg.Irc.Nickpass) } } identified: irc.ClearCallback("NOTICE") close(identwaiter) } return }
// JoinCallback triggers on JOIN event which happens whenever *anyone* joins func JoinCallback(con *irc.Connection) { con.AddCallback("JOIN", func(e *irc.Event) { if e.Nick != config.IRCNick { // Exclude bot joins log.Printf("%s joined %s", e.Nick, e.Arguments[0]) } else { log.Printf(e.Message()) } }) }
// WelcomeCallback is a PrivMsgCallback for the welcome event 001, join channels when we're logged in func WelcomeCallback(con *irc.Connection) { con.AddCallback("001", func(e *irc.Event) { con.Privmsgf("NickServ", "identify %s", config.IRCNickPass) time.Sleep(time.Second * 15) // Need to wait a little for the registration to take effect for _, room := range config.IRCChannels { con.Join(room) log.Printf("Connected to channel %s\n", room) } }) }
// PrivMsgCallback (PRIVMSG) is really any message in IRC that the bot sees func PrivMsgCallback(con *irc.Connection) { con.AddCallback("PRIVMSG", func(e *irc.Event) { message := e.Arguments[1] channel := e.Arguments[0] log.Printf("%s said: %s\n", e.Nick, message) // Look for urls in messages urls := urlRegex.FindAllString(message, -1) if len(urls) > 0 { for _, url := range urls { messageCh := make(chan string, 1) go scrapePage(url, messageCh) con.Privmsg(channel, <-messageCh) } } }) }
func fetchPageTitles(irc *goirc.Connection) { irc.AddCallback("PRIVMSG", func(e *goirc.Event) { rehttp := regexp.MustCompile("(https?://.+)") if rehttp.MatchString(e.Message()) { url := rehttp.FindStringSubmatch(e.Message()) if len(url) < 2 { return } title := fetchTitle(url[1]) log.Printf("Retrieved tile '%s' from url %s\n", title, url[1]) if title != "" { irc.Privmsgf(cfg.Irc.Channel, "Title: %s", title) } } }) return }
// ErrorCallback should log anytime an error happens func ErrorCallback(con *irc.Connection) { con.AddCallback("ERROR", func(e *irc.Event) { log.Printf("Error: %#v\n", e) }) }
func main() { var ( irc *goirc.Connection err error ) var configFile = flag.String("c", "r2d2.cfg", "Load configuration from file") flag.Parse() _, err = os.Stat(*configFile) if err != nil { log.Fatal("%v", err) os.Exit(1) } err = gcfg.ReadFileInto(&cfg, *configFile) if err != nil { log.Fatal("Error in configuration file: %v", err) os.Exit(1) } irc = goirc.IRC(cfg.Irc.Nick, cfg.Irc.Nick) irc.UseTLS = cfg.Irc.TLS irc.VerboseCallbackHandler = cfg.Irc.Debug irc.Debug = cfg.Irc.Debug err = irc.Connect(cfg.Irc.Server) if err != nil { log.Fatal("Connection to IRC server failed: %v", err) os.Exit(1) } // block while performing authentication handleAuth(irc) // we are identified, let's continue if cfg.Irc.ChannelPass != "" { // if a channel pass is used, craft a join command // of the form "&<channel>; <key>" irc.Join(cfg.Irc.Channel + " " + cfg.Irc.ChannelPass) } else { irc.Join(cfg.Irc.Channel) } if cfg.Irc.Debug { irc.Privmsg(cfg.Irc.Channel, "beep beedibeep dibeep") } go watchGithub(irc) go watchUntappd(irc) go fetchPageTitles(irc) initMaxmind() // add callback that captures messages sent to bot terminate := make(chan bool) irc.AddCallback("PRIVMSG", func(e *goirc.Event) { re := regexp.MustCompile("^" + cfg.Irc.Nick + ":(.+)$") if re.MatchString(e.Message()) { parsed := re.FindStringSubmatch(e.Message()) if len(parsed) != 2 { return } req := strings.Trim(parsed[1], " ") resp := handleRequest(e.Nick, req, irc) if resp != "" { irc.Privmsgf(cfg.Irc.Channel, "%s: %s", e.Nick, resp) } } }) <-terminate irc.Loop() irc.Disconnect() }