func setUp(t *testing.T, start ...bool) (*Conn, *testState) { ctrl := gomock.NewController(t) st := state.NewMockStateTracker(ctrl) r := event.NewRegistry() ed := event.NewMockEventDispatcher(ctrl) nc := MockNetConn(t) c := Client("test", "test", "Testing IRC", r) logging.SetLogLevel(logging.LogFatal) c.ED = ed c.ST = st c.st = true c.sock = nc c.Flood = true // Tests can take a while otherwise c.Connected = true if len(start) == 0 { // Hack to allow tests of send, recv, write etc. // NOTE: the value of the boolean doesn't matter. c.postConnect() // Sleep 1ms to allow background routines to start. <-time.After(1e6) } return c, &testState{ctrl, st, ed, nc, c} }
func setUp(t *testing.T, start ...bool) (*Conn, *testState) { ctrl := gomock.NewController(t) st := state.NewMockStateTracker(ctrl) r := event.NewRegistry() ed := event.NewMockEventDispatcher(ctrl) l := logging.NewMockLogger(ctrl) nc := MockNetConn(t) c := Client("test", "test", "Testing IRC", r, l) // We don't want to have to specify s.log.EXPECT().Debug() for all the // random crap that gets logged. This mocks it all out nicely. ctrl.RecordCall(l, "Debug", gomock.Any(), gomock.Any()).AnyTimes() c.ED = ed c.ST = st c.st = true c.sock = nc c.Flood = true // Tests can take a while otherwise c.Connected = true if len(start) == 0 { // Hack to allow tests of send, recv, write etc. // NOTE: the value of the boolean doesn't matter. c.postConnect() // Sleep 1ms to allow background routines to start. <-time.After(1e6) } return c, &testState{ctrl, l, st, ed, nc, c} }
// 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() 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) }
func main() { flag.Parse() log := logging.NewFromFlags() reg := event.NewRegistry() if *server == "" { //Don't call log.Fatal as we don't want a backtrace in this case log.Error("--server option required. \nOptions are:\n") flag.PrintDefaults() os.Exit(1) } // Connect to mongo db, err := db.Connect("localhost") if err != nil { log.Fatal("mongo dial failed: %v\n", err) } defer db.Session.Close() // Initialise the factoid driver (which currently acts as a plugin mgr too). fd := factdriver.FactoidDriver(db, log) // Configure IRC client irc := client.Client(*nick, "boing", "not really sp0rkle", reg, log) irc.SSL = *ssl // Initialise bot state bot := bot.Bot(irc, fd, log) bot.AddChannels(strings.Split(*channels, ",")) // Add drivers bot.AddDriver(bot) bot.AddDriver(fd) bot.AddDriver(calcdriver.CalcDriver(log)) bot.AddDriver(decisiondriver.DecisionDriver(log)) bot.AddDriver(quotedriver.QuoteDriver(db, log)) bot.AddDriver(netdriver.NetDriver(log)) bot.AddDriver(seendriver.SeenDriver(db, log)) bot.AddDriver(urldriver.UrlDriver(db, log)) // Register everything (including http handlers) bot.RegisterAll() // Start up the HTTP server go http.ListenAndServe(*httpPort, nil) // Connect loop. quit := false for !quit { if err := irc.Connect(*server); err != nil { log.Fatal("Connection error: %s", err) } quit = <-bot.Quit } if bot.ReExec() { // Calling syscall.Exec probably means deferred functions won't get // called, so disconnect from mongodb first for politeness' sake. db.Session.Close() // If sp0rkle was run from PATH, we need to do that lookup manually. fq, _ := exec.LookPath(os.Args[0]) log.Warn("Re-executing sp0rkle with args '%v'.", os.Args) err := syscall.Exec(fq, os.Args, os.Environ()) if err != nil { // hmmmmmm log.Fatal("Couldn't re-exec sp0rkle: %v", err) } } }
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 } } }