Example #1
0
func main() {
	config := loadConfig()

	irclog.SetLogger(LogrusAdapter{*log})

	ircCfg := config.Network.GoIrcConfig()
	c := irc.Client(ircCfg)
	c.EnableStateTracking()

	module.InitModules(c, config)

	c.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) {
		for _, cmd := range config.Network.OnConnectCmds {
			conn.Raw(cmd)
		}
		for channel, _ := range config.Network.Channels {
			conn.Join(channel)
		}
	})

	quit := make(chan bool)
	c.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	if err := c.ConnectTo(config.Network.Server); err != nil {
		panic(err)
	}

	// Wait for disconnect
	<-quit

}
Example #2
0
// Simple adapter to utilise my logging package with goirc.
// Just import this package alongside goirc/client and call
// golog.Init() in your main() to set things up.
func Init() {
	l := log.NewFromFlags()
	l.SetDepth(1)
	logging.SetLogger(l)
}
Example #3
0
func main() {

	zax = ZAX{}
	zax_log, _ := os.OpenFile("zax.log", os.O_CREATE|os.O_APPEND, os.ModeAppend)
	irc_logging.SetLogger(IrcLogger{})

	log_file := logging.NewLogBackend(zax_log, "", 0)
	log_file_f := logging.NewBackendFormatter(log_file, format)

	log_stdout := logging.NewLogBackend(os.Stdout, "", 0)
	log_stdout_f := logging.NewBackendFormatter(log_stdout, format)
	log_stdout_levelled := logging.AddModuleLevel(log_stdout_f)
	log_stdout_levelled.SetLevel(logging.INFO, "")

	logging.SetBackend(log_stdout_levelled, log_file_f)

	last_url = ""

	m := []Message{}
	e := []Event{}
	u := []Url{}
	history = IrcHistory{}
	history.data = HistoryData{m, e, u}
	history.userdata = make(map[string]*HistoryData)
	log.Notice("Loading config...")

	file, _ := os.Open("conf.json")
	decoder := json.NewDecoder(file)
	config = Config{}
	err := decoder.Decode(&config)
	if err != nil {
		log.Errorf("Error loading config: %s", err.Error())
		os.Exit(-1)
	}

	log.Notice("Config loaded.")
	log.Notice("Opening history...")
	time_history := time.Now()
	file_history, err = os.OpenFile("history.log", os.O_CREATE, os.ModeAppend)
	if err != nil {
		log.Errorf("Unable to open history.log: %s", err.Error())
		os.Exit(-1)
	}
	log.Notice("Loading history...")
	reader := bufio.NewScanner(file_history)
	file_history_writer = bufio.NewWriter(file_history)
	for reader.Scan() {
		l := reader.Text()
		is_url := strings.HasPrefix(l, "url")
		is_msg := strings.HasPrefix(l, "msg")
		is_event := strings.HasPrefix(l, "event")
		parts := []string{}
		user := ""
		channel := ""
		timestamp := time.Time{}

		if is_url || is_msg || is_event {
			parts = strings.Split(l, ",")
			ts, _ := strconv.ParseInt(parts[1], 10, 64)
			timestamp = time.Unix(ts, 0)
			user = parts[2]
			channel = parts[3]
			if !history.IsUserInit(user) {
				history.InitUser(user)
			}
		}
		if strings.HasPrefix(l, "event") {
			evt_s := parts[4]
			data := ""
			if len(parts) == 6 {
				data = parts[5]
			}

			event := Event{evt_s, user, data, channel, timestamp}
			history.userdata[user].Events = append(history.userdata[user].Events, event)
			history.data.Events = append(history.data.Events, event)
		}

		if strings.HasPrefix(l, "msg") {
			text := ""
			for i := 4; i < len(parts); i++ {
				text = text + parts[i]
			}
			msg := Message{text, user, channel, timestamp}
			history.userdata[user].Messages = append(history.userdata[user].Messages, msg)
			history.data.Messages = append(history.data.Messages, msg)
		}

		if strings.HasPrefix(l, "url") {
			url := Url{parts[3], timestamp}
			history.userdata[user].Urls = append(history.userdata[user].Urls, url)
			history.data.Urls = append(history.data.Urls, url)
		}
	}
	elapsed := time.Since(time_history)
	log.Noticef("History loaded %d events, %d urls and %d messages in %f seconds.\n", len(history.data.Events), len(history.data.Urls), len(history.data.Messages), elapsed.Seconds())
	log.Notice("Initializing IRC connection.")

	// Init IRC connection
	log.Noticef("Creating IRC cfg. Server: %s, Use SSL: %t, Nickname %s", config.Server, config.SSL, config.Nickname)
	cfg := irc.NewConfig(config.Nickname)
	cfg.SSL = config.SSL
	cfg.SSLConfig = &tls.Config{}
	cfg.SSLConfig.InsecureSkipVerify = config.SSLIgnoreInsecure
	cfg.Me.Ident = "ZAX"
	cfg.Me.Name = "ZAX"
	cfg.Version = "ZAX"
	cfg.Server = config.Server
	cfg.NewNick = func(n string) string { return n + "^" }
	quit := make(chan bool)
	c := irc.Client(cfg)
	c.EnableStateTracking()
	c.Connect()
	zax.IrcClient = c
	zax.IrcConfig = cfg
	c.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			for i := 0; i < len(config.Channels); i++ {
				ch := config.Channels[i]
				c.Join(ch.Chan, ch.Password)
			}
		})
	c.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Notice("Disconnected")
			quit <- true
		})
	c.HandleFunc(irc.JOIN,
		func(conn *irc.Conn, line *irc.Line) {
			log.Infof("[%s] %s (%s@%s) has joined.", line.Target(), line.Nick, line.Ident, line.Host)
			history.AddEvent(line.Nick, "join", "", line.Target())
		})

	c.HandleFunc(irc.QUIT,
		func(conn *irc.Conn, line *irc.Line) {
			log.Infof("[%s] %s (%s@%s) has quit.", line.Target(), line.Nick, line.Ident, line.Host)
			history.AddEvent(line.Nick, "quit", line.Text(), "")
		})

	c.HandleFunc(irc.PING,
		func(conn *irc.Conn, line *irc.Line) {
			log.Debug("PING.")
		})

	c.HandleFunc(irc.PRIVMSG,
		func(conn *irc.Conn, line *irc.Line) {
			sender_host := line.Host
			sender := line.Nick
			text := line.Text()
			channel := line.Target()
			target := line.Target()
			reply_to := line.Target()

			if line.Target() == config.Nickname {
				reply_to = sender
			}
			// Remove control characters
			text = strings.Replace(text, "", "", -1)
			text = strings.Replace(text, "", "", -1)

			log.Noticef("[%s] %s: %s", target, sender, text)

			history.AddMessage(sender, target, text)
			if len(config.ReportChan) > 0 {
				zax.Privmsg(config.ReportChan, fmt.Sprintf("[%s] %s: %s", target, sender, text))
			}

			args := strings.Split(text, " ")

			cmd_admin := []string{"%%", "<<"}

			// Check if admin
			if is_command(text, cmd_admin) {
				re_adm := regexp.MustCompile("(nick|host):(.+)")
				criteria := re_adm.FindStringSubmatch(config.Admin)
				//is_admin := false
				match_str := ""
				if criteria == nil {
					log.Debug("Unable to parse admin criteria.")
					return
				}
				re_adm_eval := regexp.MustCompile(criteria[2])
				if criteria[1] == "nick" {
					match_str = sender
				}
				if criteria[1] == "host" {
					match_str = sender_host
				}
				if !re_adm_eval.MatchString(match_str) {
					log.Debugf("Didn't pass the criteria: %s:%s", criteria[1], criteria[2])
					return
				}
				log.Debugf("Passed the criteria: %s:%s with %s", criteria[1], criteria[2], match_str)
				if text == "<<" {
					zax.Quit(get_quit_msg())
				}
				if strings.HasPrefix(text, "%%") {
					if len(args) == 4 {
						if args[1] == "opt" {
							if args[2] == "process_urls" {
								if args[3] == "on" {
									config.ProcessUrls = true
								}
								if args[3] == "off" {
									config.ProcessUrls = false
								}
							}
						}
					}

				}

			}

			cmd_rand := []string{".r", ".random"}
			cmd_steam := []string{".s", ".steam"}
			cmd_game := []string{".g", ".game"}
			cmd_url := []string{".u", ".url"}
			cmd_msg := []string{".m", ".msg"}
			cmd_seen := []string{"!"}
			cmd_help := []string{"?h"}

			if is_command(text, cmd_help) {
				reply_msg := ""
				if text == "?h" {
					reply_msg = "Cmds: [[.g(ame) .r(andom) .s(team) .u(rl) .m(sg) !]] -- Type ?h <cmd> for more info."
				}
				if len(args) > 1 {
					if args[1] == "!" {
						reply_msg = "Checks when user was last seen. Syntax: !<username>"
					}
					if is_command(args[1], cmd_rand) {
						reply_msg = "Generate random number. Syntax: .random <min> <max>"
					}
					if is_command(args[1], cmd_steam) {
						if len(args) == 3 {
							if args[2] == "symbols" {
								reply_msg = "MP=MultiPlayer, SP=SinglePlayer, CO=Co-op VAC=Valve Anti-Cheat, TC=Trading Card, Ach=Achievments, EA=Early Access, WS=Workshop support"
							}
						} else {
							reply_msg = "Search steam. For result symbols type '?h .s symbols' Syntax: .steam [ find | latest | random | trending | appid] <expression>"
						}
					}
					if is_command(args[1], cmd_game) {
						reply_msg = "Search for game info. Syntax: .game <query>"
					}
					if is_command(args[1], cmd_msg) {
						reply_msg = "Search message log. Syntax: .msg [ find | latest | random ] <expression>"
					}
					if is_command(args[1], cmd_url) {
						reply_msg = "Search URL log. Syntax: .url [ find | latest | random ] <expression>"
					}
				}
				zax.Privmsg(reply_to, reply_msg)
				return
			}
			if is_command(text, cmd_seen) {
				log.Debug("Executing seen command.")
				seen_user := strings.Replace(args[0], "!", "", -1)
				if seen_user == "" {
					log.Debug("No user was specified.")
					return
				}
				if seen_user == sender {
					log.Debug("Sender same as specified seen user, insult.")
					zax.Privmsg(reply_to, get_insult())
				}
				state := c.StateTracker().GetNick(seen_user)
				if state != nil {
					user_channels := state.Channels
					for i := 0; i < len(config.Channels); i++ {
						ch := config.Channels[i]
						_, exists := user_channels[ch.Chan]
						if exists {
							if ch.Chan == channel {
								zax.Privmsg(reply_to, get_insult())
								log.Notice("seen_user is here now.")
							} else {
								sender_state := c.StateTracker().GetNick(sender)
								_, exists := sender_state.Channels[ch.Chan]
								if exists {
									zax.Privmsg(reply_to, seen_user+" is on "+ch.Chan)
								} else {
									zax.Privmsg(reply_to, "Yeah, somewhere... can't tell you where though.")
								}
							}
						}
					}
				}
				time_seen := time.Time{}
				data, found := history.userdata[seen_user]
				if !found {
					zax.Privmsg(channel, get_user_not_exists())
					return
				}
				log.Debug("Finding latest event/msg...")
				action := ""
				evt := Event{}
				msg := Message{}

				if data.Events != nil && len(data.Events) > 0 {
					evt = data.Events[len(data.Events)-1]
				}
				if data.Messages != nil && len(data.Messages) > 0 {
					msg = data.Messages[len(data.Messages)-1]
				}
				is_event := false
				if evt.Event != "" {
					time_seen = evt.Timestamp
					is_event = true
				} else if msg.Msg != "" {
					time_seen = msg.Timestamp
					is_event = false
				} else {
					if evt.Timestamp.Unix() > msg.Timestamp.Unix() {
						time_seen = evt.Timestamp
						is_event = true
					} else {
						time_seen = msg.Timestamp
						is_event = false
					}
				}
				if is_event {
					if evt.Event == "quit" {
						action = "quitting"
					}
					if evt.Event == "join" {
						action = "joining"
					}
				} else {
					action = "writing: \"" + msg.Msg + "\""
				}
				log.Debugf("Found latest event %s at %d", action, time_seen.Unix())

				duration := time.Since(time_seen)
				days := 0
				hours := 0
				sec := 0
				min := 0

				log.Debugf("User %s seen hours: %.1f, minutes: %.1f, seconds %.1f ago.", seen_user, duration.Hours(), duration.Minutes(), duration.Seconds())

				if duration.Hours() > 24 {
					days = int(duration.Hours()) / 24
					hours = int(duration.Hours()) % 24
					min = 0
				} else if duration.Minutes() > 60 {
					hours = int(duration.Hours())
					min = int(duration.Minutes()) % 60
				} else if duration.Seconds() > 60 {
					min = int(duration.Minutes())
					sec = int(duration.Seconds()) % 60
				} else {
					sec = int(duration.Seconds())
				}

				if duration.Hours() > 24 {
					days = int(duration.Hours()) / 24
					hours = int(duration.Hours()) % 24
					min = 0
				}

				times := []string{}
				if days > 0 {
					times = append(times, strconv.Itoa(days)+" day(s)")
				}
				if hours > 0 {
					times = append(times, strconv.Itoa(hours)+" hour(s)")
				}
				if min > 0 {
					times = append(times, strconv.Itoa(min)+" minute(s)")
				}
				if sec > 0 {
					times = append(times, strconv.Itoa(sec)+" second(s)")
				}

				time_str := strings.Join(times, ", ")
				zax.Privmsg(reply_to, fmt.Sprintf("%s was last seen %s ago %s.", seen_user, time_str, action))
			}

			if is_command(text, cmd_game) {
				query := ""
				for i := 1; i < len(args); i++ {
					query += " " + args[i]
				}
				games, success := games.FindGames(query, config.UserAgent)
				if success {
					zax.Privmsg(reply_to, fmt.Sprintf("%s (%s) - %s\n", games[0].Name, games[0].Year, games[0].Url))
				}
			}

			if is_command(text, cmd_url) {
				var url Url
				var urls []Url
				urls = history.data.Urls

				is_cmd_last := args[1] == "last" || args[1] == "l"
				is_cmd_random := args[1] == "random" || args[1] == "r"
				is_cmd_find := args[1] == "find" || args[1] == "f"

				if is_cmd_last || is_cmd_random && len(args) == 3 {
					user_urls := history.userdata[args[2]].Urls
					if user_urls != nil {
						urls = user_urls
					}
				}
				if is_cmd_last {
					url = urls[len(urls)-1]
				}
				if is_cmd_random {
					url = urls[rand_int(0, len(urls)-1)]
				}
				if is_cmd_find {
					expr := ""
					for i := 2; i < len(args); i++ {
						expr += expr + args[i]
					}
					re := regexp.MustCompile(expr)
					for _, i_url := range urls {
						match := re.FindStringSubmatch(i_url.Url)
						if match != nil {
							url = i_url
						}
					}
				}
				if url.Url == "" {
					return
				}
				t := url.Timestamp
				zax.Privmsg(reply_to, fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d] %v", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), url.Url))
				return
			}
			if is_command(text, cmd_msg) {
				var msg Message
				var msgs []Message

				msgs = history.data.Messages

				is_cmd_last := args[1] == "last" || args[1] == "l"
				is_cmd_random := args[1] == "random" || args[1] == "r"
				is_cmd_find := args[1] == "find" || args[1] == "f"

				if is_cmd_last || is_cmd_random && len(args) == 3 {
					user_msgs := history.userdata[args[2]].Messages
					if user_msgs != nil {
						msgs = user_msgs
					}
				}

				if is_cmd_last {
					msg = msgs[len(msgs)-1]
				}
				if is_cmd_random {
					msg = msgs[rand_int(0, len(msgs)-1)]
				}
				if is_cmd_find {
					expr := ""
					for i := 2; i < len(args); i++ {
						add := args[i]
						if i != 2 {
							add = " " + add
						}
						expr += add
					}
					re := regexp.MustCompile(expr)
					for _, i_msg := range msgs {
						match := re.FindStringSubmatch(i_msg.Msg)
						if match != nil {
							if !(strings.Contains(i_msg.Msg, fmt.Sprintf("%s %s", args[0], args[1]))) {
								msg = i_msg
							}
						}
					}
				}
				if msg.Msg == "" {
					return
				}
				t := msg.Timestamp
				zax.Privmsg(reply_to, fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d] %v: %v", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), msg.User, msg.Msg))
			}
			if is_command(text, cmd_rand) {
				if len(args) < 3 {
					return
				}
				min, err := strconv.ParseInt(args[1], 10, 32)
				if err != nil {
					log.Debug("Failed to parse min.")
					return
				}
				max, err := strconv.ParseInt(args[2], 10, 32)
				if err != nil {
					log.Debug("Failed to parse max.")
					return
				}
				if min > max {
					return
				}
				zax.Privmsg(reply_to, "What about... "+strconv.Itoa(rand_int(int(min), int(max))))
			}
			if is_command(text, cmd_steam) {
				if len(args) < 2 {
					return
				}
				subcommand := args[1]
				success := false
				steam_appid := 0
				var err error

				steam_latest_url := "http://store.steampowered.com/search/?sort_by=Released_DESC&tags=-1&category1=998&page="

				if subcommand == "latest" || subcommand == "l" {
					steam_appid, success = steam.SearchSteampowered(steam_latest_url+"1", 0)
				}
				if subcommand == "random" || subcommand == "r" {
					page := strconv.Itoa(rand_int(1, 286))
					steam_appid, success = steam.SearchSteampowered(steam_latest_url+page, -2)
				}
				if subcommand == "trending" || subcommand == "t" {
					apps, suc := steam.GetTrending(config.UserAgent)
					if suc {
						app := apps[0]
						zax.Privmsg(reply_to, fmt.Sprintf("[Steamcharts] %s [%s increase in players last 24h] %d current players. Type '.s a %d' to get more info.", app.Name, app.Increase, app.Players, app.Id))
						return
					}
				}

				if subcommand == "appid" || subcommand == "a" {
					steam_appid, err = strconv.Atoi(args[2])
					success = (err == nil)
				}
				if subcommand == "find" || subcommand == "f" {
					re := regexp.MustCompile(fmt.Sprintf("%s %s ([[:alnum:]'*!_ ]+)", args[0], args[1]))
					match := re.FindStringSubmatch(text)
					if match == nil || len(match) == 0 {
						log.Debug("Doesn't match.")
						return
					}
					log.Debugf("matched term: %s", match[1])
					search_url := "http://store.steampowered.com/search/?snr=&term=" + match[1]
					log.Debugf("Search URL: %s", search_url)
					steam_appid, success = steam.SearchSteampowered(search_url, 0)
				}
				if success {
					log.Info("Found appid %d, retrieving info...", steam_appid)
					app, success2 := steam.GetAppInfo(steam_appid, config.UserAgent)
					if success2 {
						rating_str := ""
						if app.Reviews > 0 {
							rating_str = fmt.Sprintf("| %.1f%s rating (%d reviews)", app.Rating, "%", app.Reviews)
						}
						os_str := ""
						if app.OS("") != "" {
							os_str = fmt.Sprintf("%s - [%s]", app.OS("/"), app.Features("/"))
						}
						price := ""
						if app.PriceDiscount != "" {
							price = "| " + app.PriceDiscount
						} else {
							if app.Price != "" {
								price = "| " + app.Price
							}
						}
						base_str := ""
						if app.ReleaseYear != "" && app.Developer != "" {
							base_str = fmt.Sprintf("(%s by \"%s\")", app.ReleaseYear, app.Developer)
						}
						info := fmt.Sprintf("[http://steamspy.com/app/%d/] \"%s\" %s %s %s %s", app.Id, app.Name, base_str, os_str, rating_str, price)
						zax.Privmsg(reply_to, info)
					} else {
						log.Error("Failed to retrieve steamapp info.")
					}

				} else {
					log.Notice("Failed to retrieve appid from search.")
				}
			}
			// Handle URLs
			if !(sender == "Wipe" && (strings.Contains(text, "Steam") || strings.Contains(text, "YouTube"))) && config.ProcessUrls {
				log.Debug("Looking for URLs...")
				urls := xurls.Relaxed.FindAllString(text, -1)
				for i := 0; i < len(urls); i++ {
					url := urls[i]
					log.Debugf("Found reddit url: %s", url)
					history.AddUrl(sender, url)

					if url == last_url {
						log.Debugf("Matches same url (%s) as last time, ignore.", last_url)
						continue
					}
					reddit, success := reddit.Search(url)
					if success {
						zax.Privmsg(reply_to, reddit)
					} else {
						log.Debug("Failed to retrieve reddit URL for the link.")
					}
					last_url = url
				}
			}
			time.Sleep(10 * time.Millisecond)
		})

	<-quit
	log.Notice("Closing history.log")
	file_history.Close()
	time.Sleep(1000 * time.Millisecond)
}
Example #4
0
func Init() {
	logging.SetLogger(GLogger{})
}
Example #5
0
// The main program logic.
func main() {
	// Load configuration path from flags
	flag.Parse()

	// Initialize the logger
	logger = glogging.GLogger{}
	logging.SetLogger(logger)

	// Check if we're supposed to generate a default config
	if *generateDefault {
		logger.Debug("Saving default configuration...")
		if err := defaultConfiguration.Save(*configPath); err != nil {
			logger.Error("Failed at saving default configuration: %v", err)
			os.Exit(1)
		}
		logger.Info("Saved default configuration.")
		os.Exit(0)
	}

	// Check if we're supposed to migrate an old config
	if *migratePath != "" {
		logger.Debug("Migrating old configuration...")
		if c, err := LoadV1Config(*migratePath); err != nil {
			logger.Error("Failed to load old configuration: %v", err)
			os.Exit(1)
		} else {
			newC := c.Migrate()
			if err := newC.Save(*configPath); err != nil {
				logger.Error("Migration failed: %v", err)
				os.Exit(1)
			}
			if err := newC.Validate(); err != nil {
				logger.Warn("Migration successful but found errors while "+
					"validating the new configuration, you should fix this before "+
					"running the bot: %v", err)
				os.Exit(2)
			}
		}
		logger.Info("Migration successful.")
		os.Exit(0)
	}

	// Load configuration from configuration path
	if c, err := Load(*configPath); err != nil {
		logger.Error("Can't load configuration from %v: %v\n", *configPath, err)
		os.Exit(1)
	} else {
		loadedConfiguration = c
	}

	logger.Debug("Loaded configuration will be printed below.")
	logger.Debug("%#v", loadedConfiguration)

	// Validate configuration
	if err := loadedConfiguration.Validate(); err != nil {
		logger.Error("The configuration is invalid: %v\n", err)
		os.Exit(2)
	}

	// Now initialize the bot
	logger.Info("Initializing vpnbot %v...", version)
	b := bot.NewBot(
		loadedConfiguration.Server.Address,
		loadedConfiguration.Server.SSL,
		loadedConfiguration.Nick,
		loadedConfiguration.Ident,
		[]string{})
	b.Conn().Config().Version = fmt.Sprintf("vpnbot/%v", version)
	b.Conn().Config().Recover = func(conn *client.Conn, line *client.Line) {
		if err := recover(); err != nil {
			logging.Error("An internal error occurred: %v\n%v",
				err, string(debug.Stack()))
		}
	}
	b.Conn().Config().Pass = loadedConfiguration.Server.Password
	if loadedConfiguration.Name != "" {
		b.Conn().Config().Me.Name = loadedConfiguration.Name
	}

	// Load plugins
	// TODO - Move this into its own little intelligent loader struct, maybe.
	isupportPlugin := isupport.Register(b)
	modePlugin := mode.Register(b, isupportPlugin)
	nickservPlugin := nickserv.Register(b, modePlugin)
	nickservPlugin.Username = loadedConfiguration.NickServ.Username
	nickservPlugin.Password = loadedConfiguration.NickServ.Password
	nickservPlugin.Channels = loadedConfiguration.Channels

	switch {
	case *makeTempBans: // Run in tempban dumping mode
		// Prepare channels to let us know about dumped bans
		doneChan := make(map[string]chan interface{})
		for _, channel := range loadedConfiguration.Channels {
			doneChan[strings.ToLower(channel)] = make(chan interface{}, 1)
		}

		// Load the tempban dumping plugin
		dumptempbanPlugin := dumptempban.Register(b, isupportPlugin, modePlugin)
		dumptempbanPlugin.DumpedBansFunc = func(target string, num int, err error) {
			if err != nil {
				logging.Error("Failed to dump bans for %v: %v", target, err)
			} else {
				logging.Info("Dumped %v bans for %v successfully.", num, target)
			}
			if done, ok := doneChan[strings.ToLower(target)]; ok {
				done <- nil
			}
		}

		// Start up the bot asynchronously
		go b.Run()

		// Wait for all channels to be done
		for _, done := range doneChan {
			<-done
		}
		b.Quit("Ban dumping done.")

	default: // Run normally
		// Load plugins
		autojoin.Register(b)
		adminplugin.Register(b, loadedConfiguration.Admins)
		bots.Register(b, isupportPlugin)
		tempbanPlugin := tempban.Register(b, isupportPlugin, modePlugin)
		tempbanPlugin.OldHostmasks = loadedConfiguration.OldHostmasks
		whoisPlugin := whois.Register(b, isupportPlugin)
		vpnbotPlugin := vpnbot.Register(b, whoisPlugin, isupportPlugin,
			tempbanPlugin, nickservPlugin)
		vpnbotPlugin.Admins = loadedConfiguration.Admins

		// This is to update the configuration when the bot joins channels
		b.HandleFunc("join",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel> ]

				// Make sure this is about us
				if line.Nick != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				// See if we already had this channel saved
				for _, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						return // Channel already saved
					}
				}

				// Store this channel
				logger.Info("Adding %v to configured channels", channel)
				loadedConfiguration.Channels = append(
					loadedConfiguration.Channels, channel)

				// And save to configuration file!
				loadedConfiguration.Save(*configPath)
			})
		b.HandleFunc("kick",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel>, <nick>, <reason> ]

				// Make sure this is about us
				if line.Args[1] != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				for index, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						// Delete the channel
						logger.Info("Removing %v from configured channels", savedChannel)
						loadedConfiguration.Channels = append(
							loadedConfiguration.Channels[0:index],
							loadedConfiguration.Channels[index+1:]...)

						// And save to configuration file!
						loadedConfiguration.Save(*configPath)
						return
					}
				}
			})
		b.HandleFunc("part",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel> (, <reason>) ]

				// Make sure this is about us
				if line.Nick != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				for index, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						// Delete the channel
						logger.Info("Removing %v from configured channels", savedChannel)
						loadedConfiguration.Channels = append(
							loadedConfiguration.Channels[0:index],
							loadedConfiguration.Channels[index+1:]...)

						// And save to configuration file!
						loadedConfiguration.Save(*configPath)
						return
					}
				}
			})

		// Run the bot
		b.Run()
	}
}
Example #6
0
func main() {
	flag.Parse() // parses the logging flags. TODO

	logging.SetLogger(ircLogger{log.New(os.Stdout, "[irc]", log.LstdFlags)})

	cfg := irc.NewConfig(frankconf.BotNick, frankconf.BotNick, "Frank Böterrich der Zweite")
	cfg.SSL = true
	cfg.SSLConfig = &tls.Config{InsecureSkipVerify: true}
	cfg.Flood = true
	cfg.Server = frankconf.IrcServer
	cfg.NewNick = func(n string) string { return n + "_" }
	c := irc.Client(cfg)
	c.EnableStateTracking()

	// connect
	c.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Connected as: %s\n", conn.Me().Nick)
			conn.Privmsg("nickserv", "identify "+frankconf.NickServPass)

			var instaJoin string
			if frankconf.Production {
				instaJoin = frankconf.InstaJoinProduction
			} else {
				instaJoin = frankconf.InstaJoinTesting
			}

			log.Printf("AutoJoining: %s\n", instaJoin)

			for _, cn := range strings.Split(instaJoin, " ") {
				if cn != "" {
					conn.Join(cn)
				}
			}

			// handle RSS
			frank.Rss(conn)

			// watch topics and maybe change them on midnight
			go frank.TopicChanger(conn)
		})

	// react
	c.HandleFunc("PRIVMSG",
		func(conn *irc.Conn, line *irc.Line) {
			// ignore eicar, the bot we love to hate.
			// Also ignore i3-bot.
			if line.Nick == "eicar" || line.Nick == "i3" {
				return
			}

			go frank.RaumBang(conn, line)
			go frank.UriFind(conn, line)
			go frank.Lmgtfy(conn, line)
			go frank.Karma(conn, line)
			go frank.Help(conn, line)
			go frank.ItsAlive(conn, line)
			go frank.Highlight(conn, line)
		})

	if frankconf.Verbose {
		c.HandleFunc("NOTICE",
			func(conn *irc.Conn, line *irc.Line) {
				tgt := line.Args[0]
				msg := line.Args[1]
				log.Printf("Debug NOTICE: tgt: %s, msg: %s\n", tgt, msg)
			})
	}

	c.HandleFunc("INVITE",
		func(conn *irc.Conn, line *irc.Line) {
			tgt := line.Args[0]
			cnnl := line.Args[1]

			// auto follow invites only in test mode or if asked by master
			if frankconf.Production && line.Nick != frankconf.Master {
				log.Printf("only following invites by %s in production\n", frankconf.Master)
				return
			}

			if conn.Me().Nick != tgt {
				log.Printf("WTF: received invite for %s but target was %s\n", conn.Me().Nick, tgt)
				return
			}

			log.Printf("Following invite for channel: %s\n", cnnl)
			conn.Join(cnnl)
		})

	// auto deop frank
	c.HandleFunc("MODE",
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Mode change array length: %s", len(line.Args))
			log.Printf("Mode changes: %s", line.Args)

			if len(line.Args) < 3 {
				// mode statement cannot be not in a channel, so ignore
				return
			}

			var modeop bool // true => add mode, false => remove mode
			var nickIndex int = 2
			for i := 0; i < len(line.Args[1]); i++ {
				switch m := line.Args[1][i]; m {
				case '+':
					modeop = true
				case '-':
					modeop = false
				case 'o':
					if !modeop || line.Args[nickIndex] != conn.Me().Nick {
						nickIndex += 1
						break
					}
					channel := line.Args[0]

					if strings.Contains(" "+frankconf.OpOkIn+" ", " "+channel+" ") {
						if strings.ToLower(line.Nick) != "chanserv" {
							conn.Privmsg(channel, "Unbelievable "+line.Nick+", you… https://yrden.de/f1.ogg")
						}
					} else {
						conn.Mode(channel, "+v-o", conn.Me().Nick, conn.Me().Nick)
						conn.Privmsg(channel, line.Nick+": SKYNET® Protection activated")
					}
					return
				default:
					nickIndex += 1
				}
			}
		})

	// disconnect
	quit := make(chan bool)
	c.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	// go go GO!
	if err := c.Connect(); err != nil {
		log.Fatalf("Connection error: %s\n", err)
	}

	log.Printf("Frank has booted\n")

	// Wait for disconnect
	<-quit
}