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 }
func watchUntappd(irc *goirc.Connection) { var ( userEvents []string err error ) lastCheckins := make(map[string]float64) for { for _, user := range cfg.Untappd.Users { // store the last checkin ID, to avoid printing the same checkin twice var lastcheckin float64 if _, ok := lastCheckins[user]; ok { lastcheckin = lastCheckins[user] } if cfg.Untappd.Debug { fmt.Println("query untappd activity for", user, "with last checkin set to", lastcheckin) } userEvents, lastCheckins[user], err = getUntappdActivityFor(user, lastcheckin) if err != nil { log.Println("Failed to get", user, "'s Untappd activity:", err) } else { for _, ev := range userEvents { irc.Privmsgf(cfg.Irc.Channel, "%s", ev) } } time.Sleep(60 * time.Second) } } }
func watchGithub(irc *goirc.Connection) { var err error // start the github watcher evchan := make(chan string) githubCli := makeGithubClient(cfg.Github.Token) for _, repo := range cfg.Github.Repos { splitted := strings.Split(repo, "/") if len(splitted) != 2 { irc.Privmsgf(cfg.Irc.Channel, "Invalid repository syntax '%s'. Must be <owner>/<reponame>", repo) continue } // don't run everything at once, we've got time... time.Sleep(time.Second) go func() { for { err = followRepoEvents(githubCli, splitted[0], splitted[1], evchan) if err != nil { log.Println("github follower crashed with error", err) } time.Sleep(60 * time.Second) } }() } go func() { for ev := range evchan { // no more than one post per second time.Sleep(time.Second) irc.Privmsgf(cfg.Irc.Channel, "%s", ev) } }() }
// 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) } }) }
func githubPrintReposList(irc *goirc.Connection) { list := "list of followed github repositories: " for _, repo := range cfg.Github.Repos { list += repo + ", " if len(list) > 300 { irc.Privmsgf(cfg.Irc.Channel, "%s", list) list = "" } } if len(list) > 0 { irc.Privmsgf(cfg.Irc.Channel, "%s", list) } return }
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 }
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() }