Exemple #1
0
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}
}
Exemple #2
0
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}
}
Exemple #3
0
// 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)
}
Exemple #4
0
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)
		}
	}
}
Exemple #5
0
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
		}
	}
}