Beispiel #1
0
func ItsAlive(conn *irc.Conn, line *irc.Line) {
	if line.Args[0] != conn.Me().Nick {
		// no private query, ignore
		return
	}

	msg := line.Args[1]
	if !strings.HasPrefix(msg, "msg ") {
		//~ log.Printf("unknown prefix for: %s", msg)
		// only accept valid commands
		return
	}

	if line.Nick != frankconf.Master {
		// only answer to master
		log.Printf("only answering to master %s, but was %s", frankconf.Master, line.Nick)
		return
	}

	cmd := strings.SplitN(msg, " ", 3)
	channel := cmd[1]
	msg = cmd[2]

	log.Printf("master command: posting “%s” to %s", msg, channel)
	conn.Privmsg(channel, msg)

}
Beispiel #2
0
func postTitle(conn *irc.Conn, line *irc.Line, title string, prefix string) {
	tgt := line.Args[0]

	secondsAgo := cacheGetSecondsToLastPost(title)
	if secondsAgo <= noRepostWithinSeconds {
		log.Printf("Skipping, because posted %d seconds ago (“%s”)", secondsAgo, title)
		return
	}

	if frankconf.Verbose {
		log.Printf("Title was last posted: %#v (“%s”)", secondsAgo, title)
	}

	log.Printf("nick=%s, target=%s, title=%s", line.Nick, tgt, title)
	// if target is our current nick, it was a private message.
	// Answer the users in this case.
	if tgt == conn.Me().Nick {
		tgt = line.Nick
	}
	if prefix == "" {
		prefix = "Link Info"
	} else {
		prefix = clean(prefix)
	}
	title = clean(title)
	// the IRC spec states that notice should be used instead of msg
	// and that bots should not react to notice at all. However, no
	// real world bot adheres to this. Furthermore, people who can’t
	// configure their client to not highlight them on notices will
	// complain.
	conn.Privmsg(tgt, "["+prefix+"] "+title)
}
Beispiel #3
0
func onInvite(irc *client.Conn, line *client.Line) {
	who, channel := line.Args[0], line.Args[1]
	log.Println(line.Nick, "invited bot to", channel)
	if who == irc.Me().Nick {
		// some IRCds only allow operators to INVITE, and on registered channels normally only identified users are operators
		// check anyway, since there are some corner cases where that doesn't happen
		if checkIdentified(irc, line.Nick) {
			log.Println("Accepting invite to", channel)
			irc.Join(channel)
		} else {
			irc.Notice(line.Nick, "you must be identified to invite")
			log.Println("Ignoring invite, user is not identified")
		}
	}
}
Beispiel #4
0
func Help(conn *irc.Conn, line *irc.Line) {
	if line.Args[0] != conn.Me().Nick {
		// no private query, ignore
		return
	}

	if line.Args[1] != "help" && line.Args[1] != "!help" {
		// no help request, ignore
		return
	}

	last := lastHelps[line.Nick]
	if time.Since(last).Minutes() <= 1 {
		log.Printf("User %s tried spamming for help, not answering (last request @ %v)", line.Nick, last)
		return
	}

	lastHelps[line.Nick] = time.Now()

	conn.Privmsg(line.Nick, "1. Test your IRC client’s highlighting:")
	conn.Privmsg(line.Nick, "  – /msg frank high")
	conn.Privmsg(line.Nick, "  – /msg frank high custom_text")
	conn.Privmsg(line.Nick, "  – /msg frank highpub custom_text")
	conn.Privmsg(line.Nick, "“high” sends you a private message, “highpub” posts to #test.")
	conn.Privmsg(line.Nick, "Your nick will be used unless custom_text is defined. Delay is always 5 seconds.")
	conn.Privmsg(line.Nick, " ")

	conn.Privmsg(line.Nick, "2. I won’t spoiler URLs if you add “no spoiler” to your message")
	conn.Privmsg(line.Nick, " ")

	conn.Privmsg(line.Nick, "3. There’s a karma system. You can’t vote on yourself.")
	conn.Privmsg(line.Nick, "  – thing++ # optional comment")
	conn.Privmsg(line.Nick, "  – thing-- # thing may be alphanumerical, Unicode is supported")
	conn.Privmsg(line.Nick, "  – karma for thing  //  karma thing  //  karma thing?")
	conn.Privmsg(line.Nick, " ")

	conn.Privmsg(line.Nick, "4. I’ll answer to !raum in certain channels.")
	conn.Privmsg(line.Nick, " ")

	conn.Privmsg(line.Nick, "If you need more details, please look at my source:")
	conn.Privmsg(line.Nick, "https://github.com/breunigs/frank")

}
Beispiel #5
0
func Highlight(conn *irc.Conn, line *irc.Line) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("MEGA-WTF:pkg: %v", r)
		}
	}()

	if line.Args[0] != conn.Me().Nick {
		// no private query, ignore
		return
	}

	msg := line.Args[1]
	if !strings.HasPrefix(msg, "high") {
		// no highlight request, ignore
		return
	}

	log.Printf("received highlighting request from %s\n", line.Nick)

	var highlight string
	if customTextRegex.MatchString(msg) {
		match := customTextRegex.FindStringSubmatch(msg)
		highlight = match[1]
	} else {
		highlight = line.Nick
	}

	// allow for 100ms round trip time to highlight on time
	time.Sleep(4900 * time.Millisecond)

	if strings.HasPrefix(msg, "highpub") {
		log.Printf("highlighting %s publicly for: %s\n", line.Nick, highlight)
		conn.Privmsg("#test", "highlight test: "+highlight)
	} else {
		log.Printf("highlighting %s privately for: %s\n", line.Nick, highlight)
		conn.Privmsg(line.Nick, highlight)
	}
}
Beispiel #6
0
// answers a user with the current karma for a given thing
func answer(conn *irc.Conn, line *irc.Line) {
	tgt := line.Args[0]
	msg := line.Args[1]

	if !karmaAnswerRegex.MatchString(msg) {
		return
	}

	match := karmaAnswerRegex.FindStringSubmatch(msg)

	if len(match) != 2 || match[1] == "" {
		log.Printf("WTF: karma answer regex somehow failed and produced invalid results")
		return
	}

	score := strconv.Itoa(data[strings.ToLower(match[1])])

	// if we were the target, it was a private message. Answer user instead
	if tgt == conn.Me().Nick {
		tgt = line.Nick
	}
	conn.Privmsg(tgt, "[Karma] "+match[1]+": "+score)
}
Beispiel #7
0
func reportAllNowPlaying(irc *client.Conn, asker, channel string) {
	if !(strings.HasPrefix(channel, "#") || strings.HasPrefix(channel, "&")) {
		log.Println("User", asker, "asked What's Playing...... via PM")
		irc.Privmsg(channel, fmt.Sprintf("%s: this only works on channels", asker))
		return
	}
	log.Println("User", asker, "requested What's Playing on channel", channel)

	if !checkIdentified(irc, asker) {
		r := fmt.Sprintf("%s: you must be identified with NickServ to use this command", asker)
		log.Println(r)
		irc.Privmsg(channel, r)
		return
	}

	if _, ok := whoChannel[channel]; ok {
		log.Println("Channel", channel, "is already executing a What's Playing request")
		return
	}

	whoChannel[channel] = make(chan bool, 1)

	go irc.Who(channel)
	for _ = range whoChannel[channel] { // wait until channel is closed
	}
	delete(whoChannel, channel)

	reportChan := make(chan bool)
	totalReport := len(whoResult[channel]) - 1
	msg := fmt.Sprintf("Reporting now playing for %d nicks in channel %s", totalReport, channel)
	log.Println(msg)
	irc.Notice(asker, msg)

	for _, nick := range whoResult[channel] {
		if nick != irc.Me().Nick {
			n := nick
			go func() {
				rateLimit <- true
				reportChan <- reportNowPlaying(irc, channel, asker, n, true)
				<-rateLimit
			}()
		}
	}
	delete(whoResult, channel)

	okCount, totalCount := 0, 0
	for r := range reportChan {
		if r {
			okCount++
		}
		if totalCount++; totalCount == totalReport {
			break
		}
	}
	close(reportChan)

	msg = fmt.Sprintf("Reported for %d of %d nicks", okCount, totalCount)
	log.Println(msg)
	irc.Notice(asker, msg)

	return
}
Beispiel #8
0
func (plugin *Plugin) OnJoin(conn *client.Conn, line *client.Line) {
	logging.Info("vpnbot.Plugin: %v joined %v", line.Src, line.Target())

	if lastCheck, ok := plugin.lastCheckNicks[line.Nick]; ok && time.Now().Sub(lastCheck) < 15*time.Minute {
		// There is a check in progress or another one has been done earlier
		logging.Debug("vpnbot.Plugin: Not checking %v, last check was %v",
			line.Nick, humanize.Time(plugin.lastCheckNicks[line.Nick]))
		return
	}
	logging.Debug("vpnbot.Plugin: Checking %v...", line.Nick)
	plugin.lastCheckNicks[line.Nick] = time.Now()

	// Is this me?
	if line.Nick == conn.Me().Nick {
		logging.Debug("vpnbot.Plugin: %v is actually me, skipping.", line.Nick)
		return
	}

	// Nickname == Ident? (9 chars cut)
	if !strings.HasPrefix(nonWordCharsRegex.ReplaceAllString(line.Nick, ""),
		strings.TrimLeft(line.Ident, "~")) {
		logging.Debug("vpnbot.Plugin: %v's nick doesn't match the ident, skipping.", line.Nick)
		return
	}

	// Hostname is masked RDNS vhost/IP?
	// TODO - Use regex to avoid banning due to similar vhosts
	if !maskedAddrRegex.MatchString(line.Host) {
		// Detected custom vHost
		logging.Debug("vpnbot.Plugin: %v has a custom vhost, skipping.", line.Nick)
		return
	}

	go func() {
		botNick := line.Nick

		nobotActivityChan := make(chan string)
		defer plugin.bot.HandleFunc("privmsg",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User sent a message"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("noticed",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User sent a notice"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("part",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User left"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("part",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
				}

				if len(line.Args) > 0 {
					switch line.Args[0] {
					case "Excess flood":
						// If this was an excess flood, consider it spam that should
						// be good to ban anyways
						nobotActivityChan <- "Excess flood, banning early"
						banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host)
						// TODO - Ramp up/down the duration with increasing/decreasing activity of the bots
						plugin.banGlobal(plugin.generateBan(line.Nick, banmask,
							"Instant excess flood", 2*24*time.Hour))
					default:
						nobotActivityChan <- "User quit normally"
					}
				}
			}).Remove()

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(1 * time.Second):
		}

		// Get WHOIS info
		info, err := plugin.whois.WhoIs(line.Nick)
		if err != nil && err != whois.ErrNoSuchNick {
			logging.Warn("vpnbot.Plugin: Can't get WHOIS info for %v, skipping: %v",
				line.Nick, err.Error())
			return
		}

		// Not an oper?
		if info.IsOperator {
			logging.Debug("vpnbot.Plugin: %v is operator, skipping.",
				line.Nick)
			return
		}

		// Not away
		if info.IsAway {
			logging.Debug("vpnbot.Plugin: %v is away, skipping.", line.Nick)
			return
		}

		// Realname == Nickname?
		if info.Realname != line.Nick {
			logging.Debug(
				"vpnbot.Plugin: %v's nick doesn't match the realname, skipping.",
				line.Nick)
			return
		}

		// Count of channels at least 48
		if len(info.Channels) < 48 {
			logging.Debug(
				"vpnbot.Plugin: %v is not in a high amount of channels, skipping.",
				line.Nick)
			return
		}

		// Not halfop, op, aop or owner in any channel
		for _, prefix := range info.Channels {
			if prefix == '%' || prefix == '@' ||
				prefix == '&' || prefix == '~' {
				logging.Debug(
					"vpnbot.Plugin: %v is opped in a channel, skipping.",
					line.Nick)
				return
			}
		}

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(250 * time.Millisecond):
		}

		// More expensive tests below...

		// Make sure we deal with an unregistered client

		status := plugin.nickserv.Status(line.Nick)[line.Nick]
		if status.Error != nil {
			logging.Warn("vpnbot.Plugin: Can't get auth status for %v, skipping: %v",
				line.Nick, status.Error)
			return
		}
		if status.Level >= nickserv.Status_IdentifiedViaPassword {
			logging.Debug("vpnbot.Plugin: %v is identified, skipping.",
				line.Nick)
			return
		}

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(250 * time.Millisecond):
		}

		// This is a bot we need to ban!
		banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host)
		// TODO - Ramp up/down the duration with increasing/decreasing activity of the bots
		plugin.banGlobal(plugin.generateBan(line.Nick, banmask,
			"Known pattern of ban-evading/logging bot", 2*24*time.Hour))
	}()
}