func generate_config_file(settings_filename string) { log := logging.InitFromFlags() // setup logging log.SetLogLevel(2) if _, err := os.Stat(settings_filename); err != nil { if os.IsNotExist(err) { log.Info("Creating settings file: " + settings_filename) settings := yaml.New() settings.Set("connection/irc_server", "irc.example.net") settings.Set("connection/channel", "#example") settings.Set("connection/nick", "gobot") settings.Set("connection/realname", "Go Bot") settings.Set("bot_config/rejoin_on_kick", true) settings.Set("bot_config/channel_protection", true) settings.Set("bot_config/owner", "example!example@example/example") settings.Set("bot_config/friends", sugar.List{"friend1!example@example/example", "friend2!example@example/example", "friend2!example@example/example"}) settings.Set("bitly/shorturls_enabled", true) settings.Set("bitly/username", "example") settings.Set("bitly/api_key", "xxxxxxxxxxxxxxxxxxxxx") settings.Write(settings_filename) } else { } } else { log.Info("Settings file: " + settings_filename + " already exists") } }
func Client(nick, ident, name string, r event.EventRegistry) *Conn { if r == nil { return nil } logging.InitFromFlags() conn := &Conn{ ER: r, ED: r, st: false, in: make(chan *Line, 32), out: make(chan string, 32), cSend: make(chan bool), cLoop: make(chan bool), cPing: make(chan bool), SSL: false, SSLConfig: nil, PingFreq: 3 * time.Minute, Flood: false, NewNick: func(s string) string { return s + "_" }, badness: 0, lastsent: time.Now(), } conn.addIntHandlers() conn.Me = state.NewNick(nick) conn.Me.Ident = ident conn.Me.Name = name conn.initialise() return conn }
func Trust(identity string, trusted_identities []string) bool { log := logging.InitFromFlags() for _, value := range trusted_identities { if identity == value { log.Info("Authenticated: " + identity) return true } } log.Info("Authentication failed for : " + identity) return false }
// Creates a new IRC connection object, but doesn't connect to anything so // that you can add event handlers to it. See AddHandler() for details. func SimpleClient(nick string, args ...string) *Conn { r := event.NewRegistry() l := logging.InitFromFlags() ident := "goirc" name := "Powered by GoIRC" if len(args) > 0 && args[0] != "" { ident = args[0] } if len(args) > 1 && args[1] != "" { name = args[1] } return Client(nick, ident, name, r, l) }
func Client(cfg *Config) *Conn { logging.InitFromFlags() if cfg == nil || cfg.Me == nil || cfg.Me.Nick == "" || cfg.Me.Ident == "" { logging.Fatal("irc.Client(): Both cfg.Nick and cfg.Ident must be non-empty.") } conn := &Conn{ cfg: cfg, in: make(chan *Line, 32), out: make(chan string, 32), handlers: handlerSet(), stRemovers: make([]Remover, 0, len(stHandlers)), lastsent: time.Now(), } conn.addIntHandlers() conn.initialise() return conn }
func main() { flag.Parse() logging.InitFromFlags() // Initialise bot state bot.Init() // Connect to mongo db.Init() defer db.Close() // Add drivers calcdriver.Init() decisiondriver.Init() factdriver.Init() karmadriver.Init() markovdriver.Init() quotedriver.Init() reminddriver.Init() seendriver.Init() urldriver.Init() // Start up the HTTP server go http.ListenAndServe(*httpPort, nil) // Connect the bot to IRC and wait; reconnects are handled automatically. // If we get true back from the bot, re-exec the (rebuilt) binary. if bot.Connect() { // Calling syscall.Exec probably means deferred functions won't get // called, so disconnect from mongodb first for politeness' sake. db.Close() // If sp0rkle was run from PATH, we need to do that lookup manually. fq, _ := exec.LookPath(os.Args[0]) logging.Warn("Re-executing sp0rkle with args '%v'.", os.Args) err := syscall.Exec(fq, os.Args, os.Environ()) if err != nil { // hmmmmmm logging.Fatal("Couldn't re-exec sp0rkle: %v", err) } } logging.Info("Shutting down cleanly.") }
func Client(cfg *Config) (*Conn, error) { logging.InitFromFlags() if cfg.Me == nil || cfg.Me.Nick == "" || cfg.Me.Ident == "" { return nil, fmt.Errorf("irc.Client(): Both cfg.Nick and cfg.Ident must be non-empty.") } conn := &Conn{ cfg: cfg, in: make(chan *Line, 32), out: make(chan string, 32), cSend: make(chan bool), cLoop: make(chan bool), cPing: make(chan bool), handlers: handlerSet(), stRemovers: make([]Remover, 0, len(stHandlers)), lastsent: time.Now(), } conn.addIntHandlers() conn.initialise() return conn, nil }
func main() { flag.Parse() logging.InitFromFlags() // Let's go find some mongo. db.Init() defer db.Close() qc := quotes.Init() // A communication channel of Quotes. quotes := make(chan *quotes.Quote) rows := make(chan []interface{}) // Function to feed rows into the rows channel. row_feeder := func(sth *sqlite3.Statement, row ...interface{}) { rows <- row } // Function to execute a query on the SQLite db. db_query := func(dbh *sqlite3.Database) { n, err := dbh.Execute("SELECT * FROM Quotes;", row_feeder) if err == nil { logging.Info("Read %d rows from database.\n", n) } else { logging.Error("DB error: %s\n", err) } } // Open up the quote database in a goroutine and feed rows // in on the input_rows channel. go func() { sqlite3.Session(*file, db_query) // once we've done the query, close the channel to indicate this close(rows) }() // Another goroutine to munge the rows into quotes. // This was originally done inside the SQLite callbacks, but // cgo or sqlite3 obscures runtime panics and makes fail happen. go func() { for row := range rows { parseQuote(row, quotes) } close(quotes) }() // And finally... count := 0 var err error for quote := range quotes { // ... push each quote into mongo err = qc.Insert(quote) if err != nil { logging.Error("Awww: %v\n", err) } else { if count%1000 == 0 { fmt.Printf("%d...", count) } count++ } } fmt.Println("done.") logging.Info("Inserted %d quotes.\n", count) }
func main() { // Parse flags from command line flag.Parse() log := logging.InitFromFlags() // setup logging log.SetLogLevel(2) if _, err := os.Stat(*config_file); err != nil { generate_config_file(*config_file) log.Error("You must edit the " + *config_file + " file before continuing") os.Exit(0) } //generate a config file if it isn't found if *generate_config { generate_config_file(*config_file) log.Error("You must edit the " + *config_file + " file before continuing") os.Exit(0) } // handle configuration log.Info("Read configuration file: " + *config_file) settings := yaml.New() settings.Read(*config_file) if *channel == "" { *channel = settings.Get("connection/channel").(string) log.Debug("Read channel from config file: " + *channel) } else { log.Debug("Read channel from flag: " + *channel) } if *nick == "" { *nick = settings.Get("connection/nick").(string) log.Debug("Read nick from config file: " + *nick) } else { log.Debug("Read nick from flag: " + *nick) } if *realname == "" { *realname = settings.Get("connection/realname").(string) log.Debug("Read realname from config file: " + *realname) } else { log.Debug("Read realname from flag: " + *realname) } if *irc_server == "" { *irc_server = settings.Get("connection/irc_server").(string) log.Debug("Read irc_server from config file: " + *irc_server) } else { log.Debug("Read irc_server from flag: " + *irc_server) } if *rejoin_on_kick == true { *rejoin_on_kick = settings.Get("bot_config/rejoin_on_kick").(bool) log.Debug("Read rejoin_on_kick from config file: %t ", *rejoin_on_kick) } else { log.Debug("Read rejoin_on_kick from flag: %t ", *rejoin_on_kick) } // bitly shorturl_enabled := settings.Get("bitly/shorturls_enabled").(bool) bitly_username := settings.Get("bitly/username").(string) bitly_api_key := settings.Get("bitly/api_key").(string) if shorturl_enabled { bitly.SetUser(bitly_username) bitly.SetKey(bitly_api_key) } owner_nick := to.String(settings.Get("bot_config/owner")) friends := to.List(settings.Get("bot_config/friends")) trusted_identities := make([]string, 0) trusted_identities = append(trusted_identities, owner_nick) for _, value := range friends { trusted_identities = append(trusted_identities, value.(string)) } // set up bot command event registry bot_command_registry := event.NewRegistry() reallyquit := false // Bot command handlers // addfriend addfriend_state := make(chan string) bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if line.Src == owner_nick { channel := line.Args[0] if len(commands) > 1 { target := commands[1] log.Debug("adding friend: %q", target) conn.Whois(target) log.Debug("triggering channel: %q", target) addfriend_state <- target } else { conn.Privmsg(channel, line.Nick+": use !addfriend <friend nick>") } } }), "addfriend") //save bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if line.Src == owner_nick { channel := line.Args[0] conn.Privmsg(channel, line.Nick+": saving settings") settings.Set("bot_config/friends", friends) settings.Write(*config_file) log.Info("%q", to.List(settings.Get("bot_config/friends"))) } }), "save") //reload bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if line.Src == owner_nick { channel := line.Args[0] conn.Privmsg(channel, line.Nick+": reloading settings") friends := to.List(settings.Get("bot_config/friends")) trusted_identities = make([]string, 0) trusted_identities = append(trusted_identities, owner_nick) for _, value := range friends { trusted_identities = append(trusted_identities, value.(string)) } log.Info("%q", to.List(settings.Get("bot_config/friends"))) } }), "reload") // op bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if Trust(line.Src, trusted_identities) { channel := line.Args[0] if len(commands) > 1 { target := commands[1] log.Info("Oping user: "******"+o "+target) } else { log.Info("Oping user: "******"+o "+line.Nick) } } }), "op") //deop bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if Trust(line.Src, trusted_identities) { channel := line.Args[0] if len(commands) > 1 { target := commands[1] conn.Mode(channel, "-o "+target) } else { conn.Mode(channel, "-o "+line.Nick) } } }), "deop") // kick bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if Trust(line.Src, trusted_identities) { channel := line.Args[0] if len(commands) > 1 { target := commands[1] kick_message := "get out" if len(commands) > 2 { //this doesn't work. Need to fix. kick_message = commands[2] } //do'nt kick if owner if target != strings.Split(owner_nick, "!")[0] { //don't kick if self if *nick != target { conn.Kick(channel, target, kick_message) } else { conn.Privmsg(channel, line.Nick+": why would i kick myself?") } } else { conn.Privmsg(channel, line.Nick+": why would i kick my lovely friend "+target+"?") } } else { conn.Privmsg(channel, line.Nick+": invalid command") } } }), "kick") //quit bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { if line.Src == owner_nick { quit_message := "i died" reallyquit = true if len(commands) > 1 { quit_message = commands[1] } conn.Quit(quit_message) } }), "quit") //urlshortener bot_command_registry.AddHandler(NewHandler(func(conn *irc.Conn, line *irc.Line, commands []string) { log.Info("URLS event") channel := line.Args[0] for _, long_url := range commands { work_url := long_url work_urlr := regexp.MustCompile(`^[\w-]+://([^/?]+)(/(?:[^/?]+/)*)?([^./?][^/?]+?)?(\.[^.?]*)?(\?.*)?$`) url_parts := work_urlr.FindAllStringSubmatch(work_url, -1) domain := url_parts[0][1] ext := url_parts[0][4] forbidden_extensions := "png|gif|jpg|mp3|avi|md|zip" extension_regex := regexp.MustCompile(`(` + forbidden_extensions + `)`) extension_test := extension_regex.FindAllStringSubmatch(ext, -1) title := "" url_util_channel := make(chan string) if extension_test == nil { go grab_title(work_url, url_util_channel) title = <-url_util_channel } go shorten_url(work_url, url_util_channel, bitly_username, bitly_api_key) short_url := <-url_util_channel output := "" if short_url != long_url { output = output + "<" + short_url + "> (at " + domain + ") " } if title != "" { output = output + " " + title } conn.Privmsg(channel, output) } }), "urlshortener") // create new IRC connection log.Info("create new IRC connection") irc_client := irc.SimpleClient(*nick, *realname) // IRC HANDLERS! irc_client.EnableStateTracking() irc_client.AddHandler("connected", func(conn *irc.Conn, line *irc.Line) { log.Info("connected as " + *nick) conn.Join(*channel) }) // Set up a handler to notify of disconnect events. quit := make(chan bool) irc_client.AddHandler("disconnected", func(conn *irc.Conn, line *irc.Line) { log.Info("disconnected") quit <- true }) //Handle Private messages irc_client.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) { log.Info("privmsg") irc_input := strings.ToLower(line.Args[1]) if strings.HasPrefix(irc_input, *command_char) { irc_command := strings.Split(irc_input[1:], " ") bot_command_registry.Dispatch(irc_command[0], conn, line, irc_command) } url_regex := regexp.MustCompile(`\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))`) urls := url_regex.FindAllString(irc_input, -1) if len(urls) > 0 { bot_command_registry.Dispatch("urlshortener", conn, line, urls) } }) //handle kick by rejoining kicked channel irc_client.AddHandler("KICK", func(conn *irc.Conn, line *irc.Line) { log.Info("Kicked from " + line.Args[0]) if *rejoin_on_kick { log.Info("rejoining " + line.Args[0]) conn.Join(line.Args[0]) } }) //notify on 332 - topic reply on join to channel irc_client.AddHandler("332", func(conn *irc.Conn, line *irc.Line) { log.Debug("Topic is %q, on %q ", line.Args[2], line.Args[1]) }) //notify on MODE irc_client.AddHandler("MODE", func(conn *irc.Conn, line *irc.Line) { for _, v := range line.Args { log.Info("mode: %q ", v) } }) //notify on WHOIS irc_client.AddHandler("311", func(conn *irc.Conn, line *irc.Line) { addedfriend := <-addfriend_state log.Info("addfriend channel: %q", addedfriend) if addedfriend == line.Args[1] { friend := line.Args[1] + "!" + line.Args[2] + "@" + line.Args[3] log.Debug("added friend " + friend) trusted_identities = append(trusted_identities, friend) friends = append(friends, friend) log.Debug("friends: %q", friends) conn.Privmsg(strings.Split(owner_nick, "!")[0], line.Nick+": added "+line.Args[1]+" as friend") //addfriend_state <- "" } else { log.Info("addfriend channel is empty: %q", addedfriend) } }) //notify on join irc_client.AddHandler("JOIN", func(conn *irc.Conn, line *irc.Line) { log.Info("Joined " + line.Args[0]) }) //handle topic changes irc_client.AddHandler("TOPIC", func(conn *irc.Conn, line *irc.Line) { log.Info("Topic on " + line.Args[0] + " changed to: " + line.Args[1]) }) // set up a goroutine to read commands from stdin if *generate_config == false { for !reallyquit { // connect to server if err := irc_client.Connect(*irc_server); err != nil { fmt.Printf("Connection error: %s\n", err) return } // wait on quit channel <-quit } } }
func main() { flag.Parse() logging.InitFromFlags() // Let's go find some mongo. db.Init() defer db.Close() fc := factoids.Init() // A communication channel of Factoids. facts := make(chan *factoids.Factoid) ptrs := make(chan []interface{}) rows := make(chan []interface{}) // Function to execute some queries on the SQLite db and shove the results // into the ptrs and rows channels created above. db_query := func(dbh *sqlite3.Database) { _, err := dbh.Execute("SELECT * FROM Factoids WHERE Value LIKE '%*%';", feeder(ptrs)) close(ptrs) if err != nil { logging.Error("DB error: %s", err) } n, err := dbh.Execute("SELECT * FROM Factoids;", feeder(rows)) close(rows) if err == nil { logging.Info("Read %d rows from database.", n) } else { logging.Error("DB error: %s", err) } } go func() { sqlite3.Session(*file, db_query) }() // First, synchronously read all the stuff from the ptrs channel // and build a set of all the factoid keys that are used as pointers for row := range ptrs { for _, val := range parseMultipleValues(toString(row[cValue])) { if key, _, _ := util.FactPointer(val); key != "" { ptrkeys[key] = true } } } // Now run another goroutine to munge the rows into factoids. // This was originally done inside the SQLite callbacks, but // cgo or sqlite3 obscures runtime panics and makes fail happen. go func() { for row := range rows { parseFactoid(row, facts) } close(facts) }() // And finally... count := 0 var err error for fact := range facts { // ... push each fact into mongo err = fc.Insert(fact) if err != nil { logging.Error("Awww: %v\n", err) } else { if count%1000 == 0 { fmt.Printf("%d...", count) } count++ } } fmt.Println("done.") logging.Info("Inserted %d factoids.\n", count) }
func main() { var host = flag.String("H", "", "host (or hosts) to act on") var role = flag.String("R", "", "role (or roles) to act on") var proxy = flag.String("P", "127.0.0.1", "agent to interact with") var all = flag.Bool("A", false, "act on all live nodes") var fserial = flag.Bool("s", false, "print output when commands finish") var fbinary = flag.Bool("b", false, "force binary mode output") var ftext = flag.Bool("l", false, "force line mode output") var glock = flag.String("G", "", "global lock to claim") var llock = flag.String("L", "", "local lock to claim") var dzrhost = flag.String("doozer", "localhost:8046", "host:port for doozer") var jobs = flag.Int("j", 0, "jobs to run in parallel") var tout = flag.Int("t", 20, "timeout (in seconds) for jobs") var fnowarn = flag.Bool("w", false, "suppress error text output") flag.Usage = clientUsage flag.Parse() timeout = *tout if timeout < 0 { log.Error("timeout must be 0 (no timeout) or positive") os.Exit(1) } nowarn = *fnowarn // The output selection modes. By default, we assume that if the output // is from a single machine, it's binary and we don't touch it. However, // if the user is running against multiple targets, we assume that the // output is line-based and we prefix the hostname returning each line. // // Line based defaults to interleaved. The serial flag can be used to // ask us to buffer each host's output and then dump it all at once when // that host is done. This is more useful for doing a batch job and having // easily read output later, but still running commands in parallel. // // Finally, the user can force binary mode on multi-host outputs, which // makes us not touch the output. serial = *fserial binary = *fbinary && !*ftext // Uses the nice golog package to handle logging arguments and flags // so we don't have to worry about it. log = logging.InitFromFlags() safedoozer.SetLogger(log) // Connect to our doozer host. We have to do this early because alias // validation requires doozer. dzr = safedoozer.Dial(*dzrhost) defer dzr.Close() // Do some simple argument validation. args := expandAlias(flag.Args()) if len(args) == 0 { flag.Usage() os.Exit(1) } if !isValidCommand(args[0], args[1:]) { os.Exit(1) } // Figure out localhost. var err error // If we use := below, we shadow the global, which is bad. hostname, err = os.Hostname() if err != nil { log.Error("failed getting hostname: %s", err) os.Exit(1) } hostname = strings.Split(hostname, ".")[0] if len(hostname) <= 0 { log.Error("hostname is empty!") os.Exit(1) } zmq_ctx, err = zmq.NewContext() if err != nil { log.Error("failed to init zmq: %s", err) os.Exit(1) } defer zmq_ctx.Close() // Connect to the proxy agent. This is the agent we will be having do all // of the work for us. This is probably localhost. psock = socketForIp(*proxy) if psock == nil { log.Error("unable to connect to proxy agent") os.Exit(1) } defer (*psock).Close() // Determine which nodes we will be addressing. hosts := make(map[string]bool) if *all { for _, host := range nodes() { hosts[host] = false } } else { if *host != "" { lhosts := strings.Split(*host, ",") for _, host := range lhosts { host = strings.TrimSpace(host) if len(host) > 0 { hosts[strings.TrimSpace(host)] = false } } } if *role != "" { roles := strings.Split(*role, ",") for _, role := range roles { lhosts := convertRoleToHosts(strings.TrimSpace(role)) for _, host := range lhosts { hosts[host] = false } } } if *host == "" && *role == "" { // Both empty, default to this machine. hosts[hostname] = false } } // If no hosts, bail out. if len(hosts) <= 0 { log.Error("no hosts or roles specified") os.Exit(1) } else if len(hosts) == 1 { // If we're targetting a single machine, we want to use binary mode by // default unless the user explicitly set text mode. binary = true && !*ftext } // If we have been told to get a global lock, let's try to get that now. if *glock != "" { resp := proxyCommand("global_lock", *glock) if resp != "locked" { log.Error("failed to get global lock %s: %s", *glock, resp) os.Exit(1) } defer func(lock string) { resp := proxyCommand("global_unlock", *glock) if resp != "unlocked" { log.Error("failed to release global lock %s: %s", *glock, resp) } }(*glock) } // Same, local. if *llock != "" { resp := proxyCommand("local_lock", *llock) if resp != "locked" { log.Error("failed to get local lock %s: %s", *llock, resp) os.Exit(1) } defer func(lock string) { resp := proxyCommand("local_unlock", *llock) if resp != "unlocked" { log.Error("failed to release local lock %s: %s", *llock, resp) } }(*llock) } // There are some commands which are client-only and don't run against a // given set of hosts. For these, just execute locally and then we're done. switch args[0] { case "roles": cmdRoles() case "hosts": cmdHosts() case "alias": cmdAlias(args[1:]) } // Queue up the jobs and then execute them. for host, _ := range hosts { queueJob(host, args) } runJobs(*jobs) // Returns when jobs are done. }
func main() { var myhost = flag.String("hostname", "", "this machine's hostname") var myport = flag.Int("port", 7330, "port number to listen on") var dzrhost = flag.String("doozer", "localhost:8046", "host:port for doozer") flag.Parse() // Uses the nice golog package to handle logging arguments and flags // so we don't have to worry about it. log = logging.InitFromFlags() safedoozer.SetLogger(log) log.Info("starting up - gid %s", gid) rand.Seed(int64(time.Now().Nanosecond())) // Not the best but ok? dzr = safedoozer.Dial(*dzrhost) defer dzr.Close() defer removeGlobalLocks() // see config.go for this initializeConfig() var err error // If we use := below, we shadow the global, which is bad. zmq_ctx, err = zmq.NewContext() if err != nil { log.Fatal("failed to init zmq: %s", err) } defer zmq_ctx.Close() myinfo, err := getSelfInfo() if err != nil { log.Fatal("Failed getting local information: %s", err) } if *myhost == "" { _, ok := myinfo["hostname"] if !ok { log.Fatal("getSelfInfo() did not return a hostname") } hostname = myinfo["hostname"] } else { log.Warn("user requested hostname override (command line argument)") hostname = *myhost } // Global, easy to access variable since we use it everywhere. This is safe // because it's read-only, too -- well, by definition. It isn't really. But // if it changes in maintainInfo then we exit. log.Info("client starting with hostname %s", hostname) // Now we have enough information to see if anybody else is claiming to be // this particular node. If so, we want to wait a bit and see if they // update again. If they are updating, then it is assumed they are running // okay, and we shouldn't start up again. lock := "/s/nlock/" + hostname rev := dzr.Stat(lock, nil) // If the lock is claimed, attempt to wait and see if the remote seems // to still be alive. if rev > 0 { log.Warn("node lock is claimed, waiting to see if it's lively") time.Sleep(3 * time.Second) nrev := dzr.Stat(lock, nil) if nrev > rev { log.Fatal("lock is lively, we can't continue!") } } // Safe to claim the node lock. Let's get it. log.Info("attempting to get the node lock") nrev := dzr.Set(lock, rev, fmt.Sprintf("%d", time.Now().Unix())) if nrev <= rev { log.Fatal("failed to obtain the lock") } log.Info("lock successfully obtained! we are lively.") go maintainLock(lock, nrev) // Now we want to reset the gid map, asserting that we are now the living // agent for this host. lock = "/s/gid/" + hostname rev = dzr.Stat(lock, nil) dzr.Set(lock, rev, gid) // There are certain miscellaneous tasks that need to get done somewhere, // so we use global locks to make sure that somebody is doing them. We // don't really care who. go manageGlobalFunc(5, "gf.expire-hosts", gfExpireHosts) go manageGlobalFunc(5, "gf.expire-glocks", gfExpireGlobalLocks) // Personal maintenance here. go maintainInfo(&myinfo) go maintainStanzas() runAgent(*myport) // Returns when dead. }
func init() { // This is probably a dirty hack... logging.InitFromFlags() logging.SetLogLevel(logging.LogFatal) }
func main() { flag.Parse() logging.InitFromFlags() golog.Init() // Slightly more random than 1. rand.Seed(time.Now().UnixNano() * int64(os.Getpid())) // Initialise bot state bot.Init() // Connect to mongo db.Init() defer db.Close() // Add drivers calcdriver.Init() decisiondriver.Init() factdriver.Init() karmadriver.Init() markovdriver.Init() netdriver.Init() quotedriver.Init() reminddriver.Init() seendriver.Init() statsdriver.Init() urldriver.Init() // Start up the HTTP server go http.ListenAndServe(*httpPort, nil) // Set up a signal handler to shut things down gracefully. // NOTE: net/http doesn't provide for graceful shutdown :-/ go func() { called := new(int32) sigint := make(chan os.Signal, 1) signal.Notify(sigint, syscall.SIGINT) for _ = range sigint { if atomic.AddInt32(called, 1) > 1 { logging.Fatal("Recieved multiple interrupts, dying.") } bot.Shutdown() } }() // Connect the bot to IRC and wait; reconnects are handled automatically. // If we get true back from the bot, re-exec the (rebuilt) binary. if <-bot.Connect() { // Calling syscall.Exec probably means deferred functions won't get // called, so disconnect from mongodb first for politeness' sake. db.Close() // If sp0rkle was run from PATH, we need to do that lookup manually. fq, _ := exec.LookPath(os.Args[0]) logging.Warn("Re-executing sp0rkle with args '%v'.", os.Args) err := syscall.Exec(fq, os.Args, os.Environ()) if err != nil { // hmmmmmm logging.Fatal("Couldn't re-exec sp0rkle: %v", err) } } logging.Info("Shutting down cleanly.") }
func main() { flag.Parse() logging.InitFromFlags() // Let's go find some mongo. db.Init() defer db.Close() uc := urls.Init() work := make(chan *urls.Url) quit := make(chan bool) urls := make(chan *urls.Url) rows := make(chan []interface{}) failed := 0 // If we're checking, spin up some workers if *check { for i := 1; i <= *workq; i++ { go func(n int) { count := 0 for u := range work { count++ logging.Debug("w%02d r%04d: Fetching '%s'", n, count, u.Url) res, err := http.Head(u.Url) logging.Debug("w%02d r%04d: Response '%s'", n, count, res.Status) if err == nil && res.StatusCode == 200 { urls <- u } else { failed++ } } quit <- true }(i) } } // Function to feed rows into the rows channel. row_feeder := func(sth *sqlite3.Statement, row ...interface{}) { rows <- row } // Function to execute a query on the SQLite db. db_query := func(dbh *sqlite3.Database) { n, err := dbh.Execute("SELECT * FROM urls;", row_feeder) if err == nil { logging.Info("Read %d rows from database.\n", n) } else { logging.Error("DB error: %s\n", err) } } // Open up the URL database in a goroutine and feed rows // in on the input_rows channel. go func() { sqlite3.Session(*file, db_query) // once we've done the query, close the channel to indicate this close(rows) }() // Another goroutine to munge the rows into Urls and optionally send // them to the pool of checker goroutines. go func() { for row := range rows { u := parseUrl(row) if *check { work <- u } else { urls <- u } } if *check { // Close work channel and wait for all workers to quit. close(work) for i := 0; i < *workq; i++ { <-quit } } close(urls) }() // And finally... count := 0 var err error for u := range urls { // ... push each url into mongo err = uc.Insert(u) if err != nil { logging.Error("Awww: %v\n", err) } else { if count%1000 == 0 { fmt.Printf("%d...", count) } count++ } } fmt.Println("done.") if *check { logging.Info("Dropped %d non-200 urls.", failed) } logging.Info("Inserted %d urls.", count) }