def ban(self, conn, event, reason): source = irc.client.NickMask(event.source) tags = dict((i['key'], i['value']) for i in event.tags) display_name = tags.get("display_name") or source.nick self.spammers.setdefault(source.nick.lower(), 0) self.spammers[source.nick.lower()] += 1 level = self.spammers[source.nick.lower()] if level <= 1: log.info("First offence, flickering %s" % display_name) conn.privmsg(event.target, ".timeout %s 1" % source.nick) conn.privmsg(event.target, "%s: Message deleted (first warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (display_name, reason)) elif level <= 2: log.info("Second offence, timing out %s" % display_name) conn.privmsg(event.target, ".timeout %s" % source.nick) conn.privmsg(event.target, "%s: Timeout (second warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (display_name, reason)) else: log.info("Third offence, banning %s" % display_name) conn.privmsg(event.target, ".ban %s" % source.nick) conn.privmsg(event.target, "%s: Banned for persistent spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (display_name, reason)) level = 3 today = datetime.datetime.now(config['timezone']).date().toordinal() if today != storage.data.get("spam",{}).get("date"): storage.data["spam"] = { "date": today, "count": [0, 0, 0], } storage.data["spam"]["count"][level - 1] += 1 storage.save()
def modify_commands(commands): storage.data["responses"] = { " ".join(k.lower().split()): v for k, v in commands.items() } storage.save() generate_hook()
def on_mode(self, conn, event): if irc.client.is_channel(event.target): for mode in irc.modes.parse_channel_modes(" ".join(event.arguments)): if mode[0] == "+" and mode[1] == 'o': self.mods.add(mode[2].lower()) storage.data['mods'] = list(self.mods) storage.save()
def modify_spam_rules(self, data): log.info("Setting spam rules to %r" % (data, )) storage.data['spam_rules'] = data storage.save() self.rules = [(re.compile(rule['re']), rule['message'], rule.get('type', 'spam')) for rule in storage.data['spam_rules']]
async def ban(self, conn, event, reason, bantype): source = irc.client.NickMask(event.source) display_name = event.tags.get("display-name", source.nick) if bantype == "spam": # Start lenient in case of false positives, but then escalate self.spammers.setdefault(source.nick.lower(), 0) self.spammers[source.nick.lower()] += 1 level = self.spammers[source.nick.lower()] if level <= 1: log.info("First offence, flickering %s" % display_name) conn.privmsg(event.target, ".timeout %s 1 %s" % (source.nick, reason)) conn.privmsg(source.nick, "Message deleted (first warning) for auto-detected spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) elif level <= 2: log.info("Second offence, timing out %s" % display_name) conn.privmsg(event.target, ".timeout %s 600 %s" % (source.nick, reason)) conn.privmsg(source.nick, "Timeout (second warning) for auto-detected spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) else: log.info("Third offence, banning %s" % display_name) conn.privmsg(event.target, ".ban %s %s" % (source.nick, reason)) conn.privmsg(source.nick, "Banned for persistent spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) level = 3 today = datetime.datetime.now(config['timezone']).date().toordinal() if today != storage.data.get("spam", {}).get("date"): storage.data["spam"] = { "date": today, "count": [0, 0, 0], } storage.data["spam"]["count"][level - 1] += 1 storage.save() elif bantype == "censor": # Only purges, no escalation log.info("Censor hit, flickering %s" % display_name) conn.privmsg(event.target, ".timeout %s 1 %s" % (source.nick, reason)) conn.privmsg(source.nick, "Your message was automatically deleted (%s). You have not been banned or timed out, and are welcome to continue participating in the chat. Please contact mrphlip or any other channel moderator if you feel this is incorrect." % reason)
def modify_link_spam_rules(self, data): storage.data['link_spam_rules'] = data storage.save() self.rules = [{ "re": re.compile(rule['re'], re.IGNORECASE), "message": rule['message'], "type": rule.get('type', 'spam'), } for rule in storage.data['link_spam_rules']]
def modify_spam_rules(self, data): log.info("Setting spam rules to %r" % (data,)) storage.data['spam_rules'] = data storage.save() self.rules = [ (re.compile(rule['re']), rule['message'], rule.get('type', 'spam')) for rule in storage.data['spam_rules'] ]
def on_mode(self, conn, event): if irc.client.is_channel(event.target): for mode in irc.modes.parse_channel_modes(" ".join( event.arguments)): if mode[0] == "+" and mode[1] == 'o': self.mods.add(mode[2].lower()) storage.data['mods'] = list(self.mods) storage.save()
def modify_commands(commands): log.info("Setting commands to %r" % commands) storage.data["responses"] = { " ".join(k.lower().split()): v for k, v in commands.items() } storage.save() generate_hook()
def ban(self, conn, event, reason, bantype): source = irc.client.NickMask(event.source) display_name = event.tags.get("display_name", source.nick) if bantype == "spam": # Start lenient in case of false positives, but then escalate self.spammers.setdefault(source.nick.lower(), 0) self.spammers[source.nick.lower()] += 1 level = self.spammers[source.nick.lower()] if level <= 1: log.info("First offence, flickering %s" % display_name) conn.privmsg(event.target, ".timeout %s 1" % source.nick) conn.privmsg( source.nick, "Message deleted (first warning) for auto-detected spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) yield from slack.send_message( "%s flickered for auto-detected spam (%s)" % (display_name, reason)) elif level <= 2: log.info("Second offence, timing out %s" % display_name) conn.privmsg(event.target, ".timeout %s" % source.nick) conn.privmsg( source.nick, "Timeout (second warning) for auto-detected spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) yield from slack.send_message( "%s timed out for auto-detected spam (%s)" % (display_name, reason)) else: log.info("Third offence, banning %s" % display_name) conn.privmsg(event.target, ".ban %s" % source.nick) conn.privmsg( source.nick, "Banned for persistent spam (%s). Please contact mrphlip or any other channel moderator if this is incorrect." % reason) yield from slack.send_message( "%s banned for auto-detected spam (%s)" % (display_name, reason)) level = 3 today = datetime.datetime.now( config['timezone']).date().toordinal() if today != storage.data.get("spam", {}).get("date"): storage.data["spam"] = { "date": today, "count": [0, 0, 0], } storage.data["spam"]["count"][level - 1] += 1 storage.save() elif bantype == "censor": # Only purges, no escalation log.info("Censor hit, flickering %s" % display_name) conn.privmsg(event.target, ".timeout %s 1" % source.nick) conn.privmsg( source.nick, "Your message was automatically deleted (%s). You have not been banned or timed out, and are welcome to continue participating in the chat. Please contact mrphlip or any other channel moderator if you feel this is incorrect." % reason) yield from slack.send_message(text="%s censored (%s)" % (display_name, reason))
def modify_link_spam_rules(self, lrrbot, user, data): storage.data['link_spam_rules'] = data storage.save() self._rules = [ { "re": re.compile(rule['re'], re.IGNORECASE), "message": rule['message'], } for rule in storage.data['link_spam_rules'] ]
def stat_update(lrrbot, stat, n, set_=False): game = lrrbot.get_current_game(readonly=False) if game is None: return None game.setdefault("stats", {}).setdefault(stat, 0) if set_: game["stats"][stat] = n else: game["stats"][stat] += n storage.save() return game
def modify_link_spam_rules(self, data): storage.data['link_spam_rules'] = data storage.save() self.rules = [ { "re": re.compile(rule['re'], re.IGNORECASE), "message": rule['message'], "type": rule.get('type', 'spam'), } for rule in storage.data['link_spam_rules'] ]
def check_spam(self, conn, event, message): """Check the message against spam detection rules""" if not irc.client.is_channel(event.target): return False respond_to = event.target source = irc.client.NickMask(event.source) for re, desc in self.spam_rules: matches = re.search(message) if matches: log.info("Detected spam from %s - %r matches %s" % (source.nick, message, re.pattern)) groups = { str(i + 1): v for i, v in enumerate(matches.groups()) } desc = desc % groups self.spammers.setdefault(source.nick.lower(), 0) self.spammers[source.nick.lower()] += 1 level = self.spammers[source.nick.lower()] if level <= 1: log.info("First offence, flickering %s" % source.nick) conn.privmsg(event.target, ".timeout %s 1" % source.nick) conn.privmsg( event.target, "%s: Message deleted (first warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc)) elif level <= 2: log.info("Second offence, timing out %s" % source.nick) conn.privmsg(event.target, ".timeout %s" % source.nick) conn.privmsg( event.target, "%s: Timeout (second warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc)) else: log.info("Third offence, banning %s" % source.nick) conn.privmsg(event.target, ".ban %s" % source.nick) conn.privmsg( event.target, "%s: Banned for persistent spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc)) level = 3 today = datetime.datetime.now( config['timezone']).date().toordinal() if today != storage.data.get("spam", {}).get("date"): storage.data["spam"] = { "date": today, "count": [0, 0, 0], } storage.data["spam"]["count"][level - 1] += 1 storage.save() return True return False
def set_data(lrrbot, user, data): if not isinstance(data['key'], (list, tuple)): data['key'] = [data['key']] log.info("Setting storage %s to %r" % ('.'.join(data['key']), data['value'])) # if key is, eg, ["a", "b", "c"] # then we want to effectively do: # storage.data["a"]["b"]["c"] = value # But in case one of those intermediate dicts doesn't exist: # storage.data.setdefault("a", {}).setdefault("b", {})["c"] = value node = storage.data for subkey in data['key'][:-1]: node = node.setdefault(subkey, {}) node[data['key'][-1]] = data['value'] storage.save()
def set_data(self, key, value): if not isinstance(key, (list, tuple)): key = [key] log.info("Setting storage %s to %r" % (user, '.'.join(key), value)) # if key is, eg, ["a", "b", "c"] # then we want to effectively do: # storage.data["a"]["b"]["c"] = value # But in case one of those intermediate dicts doesn't exist: # storage.data.setdefault("a", {}).setdefault("b", {})["c"] = value node = storage.data for subkey in key[:-1]: node = node.setdefault(subkey, {}) node[key[-1]] = value storage.save()
def check_subscriber(self, conn, nick, metadata): """ Whenever a user says something, update the subscriber list according to whether their message has the subscriber-badge metadata attached to it. """ is_sub = 'subscriber' in metadata.get('specialuser', set()) if not is_sub and nick in self.subs: self.subs.remove(nick) storage.data['subs'] = list(self.subs) storage.save() elif is_sub and nick not in self.subs: self.subs.add(nick) storage.data['subs'] = list(self.subs) storage.save()
def on_subscriber(self, conn, channel, user, eventtime, logo=None, monthcount=None): notifyparams = { 'apipass': config['apipass'], 'message': "%s just subscribed!" % user, 'eventtime': eventtime, 'subuser': user, 'channel': channel, } if logo is None: try: channel_info = twitch.get_info(user) except: pass else: if channel_info.get('logo'): notifyparams['avatar'] = channel_info['logo'] else: notifyparams['avatar'] = logo if monthcount is not None: notifyparams['monthcount'] = monthcount # have to get this in a roundabout way as datetime.date.today doesn't take a timezone argument today = datetime.datetime.now(config['timezone']).date().toordinal() if today != storage.data.get("storm", {}).get("date"): storage.data["storm"] = { "date": today, "count": 0, } storage.data["storm"]["count"] += 1 self.lastsubs.append(user.lower()) self.lastsubs = self.lastsubs[-10:] storage.save() conn.privmsg( channel, "lrrSPOT Thanks for subscribing, %s! (Today's storm count: %d)" % (notifyparams['subuser'], storage.data["storm"]["count"])) utils.api_request('notifications/newmessage', notifyparams, 'POST') self.subs.add(user.lower()) storage.data['subs'] = list(self.subs) storage.save()
def stormcount(lrrbot, conn, event, respond_to): """ Command: !storm Command: !stormcount Section: info Show the current storm count (the number of viewers who have subscribed today) """ today = datetime.datetime.now(config["timezone"]).date().toordinal() if today != storage.data.get("storm", {}).get("date"): storage.data["storm"] = { "date": today, "count": 0 } storage.save() conn.privmsg(respond_to, "Today's storm count: %d" % storage.data["storm"]["count"])
def set_game_name(lrrbot, conn, event, respond_to, name): """ Command: !game display NAME Section: info eg. !game display Resident Evil: Man Fellating Giraffe Change the display name of the current game to NAME. """ game = lrrbot.get_current_game(readonly=False) if game is None: conn.privmsg(respond_to, "Not currently playing any game, if they are yell at them to update the stream") return game["display"] = name storage.save() conn.privmsg(respond_to, "OK, I'll start calling %(name)s \"%(display)s\"" % game)
def check_spam(self, conn, event, message): """Check the message against spam detection rules""" if not irc.client.is_channel(event.target): return False respond_to = event.target source = irc.client.NickMask(event.source) for re, desc in self.spam_rules: matches = re.search(message) if matches: log.info("Detected spam from %s - %r matches %s" % (source.nick, message, re.pattern)) groups = {str(i + 1): v for i, v in enumerate(matches.groups())} desc = desc % groups self.spammers.setdefault(source.nick.lower(), 0) self.spammers[source.nick.lower()] += 1 level = self.spammers[source.nick.lower()] if level <= 1: log.info("First offence, flickering %s" % source.nick) conn.privmsg(event.target, ".timeout %s 1" % source.nick) conn.privmsg( event.target, "%s: Message deleted (first warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc), ) elif level <= 2: log.info("Second offence, timing out %s" % source.nick) conn.privmsg(event.target, ".timeout %s" % source.nick) conn.privmsg( event.target, "%s: Timeout (second warning) for auto-detected spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc), ) else: log.info("Third offence, banning %s" % source.nick) conn.privmsg(event.target, ".ban %s" % source.nick) conn.privmsg( event.target, "%s: Banned for persistent spam (%s). Please contact mrphlip or d3fr0st5 if this is incorrect." % (source.nick, desc), ) level = 3 today = datetime.datetime.now(config["timezone"]).date().toordinal() if today != storage.data.get("spam", {}).get("date"): storage.data["spam"] = {"date": today, "count": [0, 0, 0]} storage.data["spam"]["count"][level - 1] += 1 storage.save() return True return False
def spamcount(lrrbot, conn, event, respond_to): """ Command: !spam Command: !spamcount Section: misc Show the number of users who have been automatically banned today for spamming """ today = datetime.datetime.now(config["timezone"]).date().toordinal() if today != storage.data.get("spam", {}).get("date"): storage.data["spam"] = { "date": today, "count": [0, 0, 0], } storage.save() conn.privmsg(respond_to, "Today's spam counts: %d hits, %d repeat offenders, %d bannings" % tuple( storage.data["spam"]["count"]))
def completed(lrrbot, conn, event, respond_to): """ Command: !game completed Section: info Mark a game as having been completed. """ game = lrrbot.get_current_game(readonly=False) if game is None: conn.privmsg(respond_to, "Not currently playing any game") return game.setdefault("stats", {}).setdefault("completed", 0) game["stats"]["completed"] += 1 storage.save() emote = storage.data.get('stats', {}).get('completed', {}).get('emote', "") if emote: emote += " " conn.privmsg(respond_to, "%s%s added to the completed list" % (emote, game_name(game)))
def highlight(lrrbot, conn, event, respond_to, description): """ Command: !highlight DESCRIPTION Section: misc For use when something particularly awesome happens onstream, adds an entry on the Highlight Reel spreadsheet: https://docs.google.com/spreadsheets/d/1yrf6d7dPyTiWksFkhISqEc-JR71dxZMkUoYrX4BR40Y Note that the highlights won't appear on the spreadsheet immediately, as the link won't be available until the stream finishes and the video is in the archive. It should appear within a day. """ if not twitch.get_info()["live"]: conn.privmsg(respond_to, "Not currently streaming.") return storage.data.setdefault("staged_highlights", []) storage.data["staged_highlights"] += [{ "time": time.time(), "user": irc.client.NickMask(event.source).nick, "description": description, }] storage.save() conn.privmsg(respond_to, "Highlight added.")
def set_game_name(lrrbot, conn, event, respond_to, name): """ Command: !game display NAME Section: info eg. !game display Resident Evil: Man Fellating Giraffe Change the display name of the current game to NAME. """ game = lrrbot.get_current_game(readonly=False) if game is None: conn.privmsg( respond_to, "Not currently playing any game, if they are yell at them to update the stream" ) return game["display"] = name storage.save() conn.privmsg(respond_to, "OK, I'll start calling %(name)s \"%(display)s\"" % game)
def on_subscriber(self, conn, channel, user, eventtime, logo=None, monthcount=None): notifyparams = { "apipass": config["apipass"], "message": "%s just subscribed!" % user, "eventtime": eventtime, "subuser": user, "channel": channel, } if logo is None: try: channel_info = twitch.get_info(user) except: pass else: if channel_info.get("logo"): notifyparams["avatar"] = channel_info["logo"] else: notifyparams["avatar"] = logo if monthcount is not None: notifyparams["monthcount"] = monthcount # have to get this in a roundabout way as datetime.date.today doesn't take a timezone argument today = datetime.datetime.now(config["timezone"]).date().toordinal() if today != storage.data.get("storm", {}).get("date"): storage.data["storm"] = {"date": today, "count": 0} storage.data["storm"]["count"] += 1 self.lastsubs.append(user.lower()) self.lastsubs = self.lastsubs[-10:] storage.save() conn.privmsg( channel, "lrrSPOT Thanks for subscribing, %s! (Today's storm count: %d)" % (notifyparams["subuser"], storage.data["storm"]["count"]), ) utils.api_request("notifications/newmessage", notifyparams, "POST") self.subs.add(user.lower()) storage.data["subs"] = list(self.subs) storage.save()
def vote(lrrbot, conn, event, respond_to, vote_good, vote_bad): """ Command: !game good Command: !game bad Section: info Declare whether you believe this game is entertaining to watch on-stream. Voting a second time replaces your existing vote. The host may heed this or ignore it at their choice. Probably ignore it. """ game = lrrbot.get_current_game(readonly=False) if game is None: conn.privmsg(respond_to, "Not currently playing any game") return nick = irc.client.NickMask(event.source).nick game.setdefault("votes", {}) game["votes"][nick.lower()] = vote_good is not None storage.save() lrrbot.vote_update = respond_to, game vote_respond(lrrbot, conn, respond_to, game)
def on_subscriber(self, conn, channel, user, eventtime, logo=None, monthcount=None): notifyparams = { 'apipass': config['apipass'], 'message': "%s just subscribed!" % user, 'eventtime': eventtime, 'subuser': user, 'channel': channel, } if logo is None: try: channel_info = twitch.get_info(user) except: pass else: if channel_info.get('logo'): notifyparams['avatar'] = channel_info['logo'] else: notifyparams['avatar'] = logo if monthcount is not None: notifyparams['monthcount'] = monthcount # have to get this in a roundabout way as datetime.date.today doesn't take a timezone argument today = datetime.datetime.now(config['timezone']).date().toordinal() if today != storage.data.get("storm",{}).get("date"): storage.data["storm"] = { "date": today, "count": 0, } storage.data["storm"]["count"] += 1 self.lastsubs.append(user.lower()) self.lastsubs = self.lastsubs[-10:] storage.save() conn.privmsg(channel, "lrrSPOT Thanks for subscribing, %s! (Today's storm count: %d)" % (notifyparams['subuser'], storage.data["storm"]["count"])) utils.api_request('notifications/newmessage', notifyparams, 'POST') self.subs.add(user.lower()) storage.data['subs'] = list(self.subs) storage.save()
def autostatus_set(lrrbot, conn, event, respond_to, enable): """ Command: !autostatus on Command: !autostatus off Section: info Enable or disable automatically sending status messages when you join the channel. """ source = irc.client.NickMask(event.source) nick = source.nick.lower() enable = enable.lower() == "on" if enable: if nick not in lrrbot.autostatus: lrrbot.autostatus.add(nick) storage.data['autostatus'] = list(lrrbot.autostatus) storage.save() conn.privmsg(source.nick, "Auto-status enabled.") else: if nick in lrrbot.autostatus: lrrbot.autostatus.remove(nick) storage.data['autostatus'] = list(lrrbot.autostatus) storage.save() conn.privmsg(source.nick, "Auto-status disabled.")
def modify_spam_rules(lrrbot, user, data): storage.data['spam_rules'] = data storage.save() lrrbot.spam_rules = [(re.compile(i['re']), i['message']) for i in storage.data['spam_rules']]
def modify_explanations(commands): storage.data["explanations"] = {k.lower(): v for k, v in commands.items()} storage.save()
def modify_explanations(commands): log.info("Setting explanations to %r" % commands) storage.data["explanations"] = {k.lower(): v for k, v in commands.items()} storage.save()
def modify_explanations(commands): storage.data["explanations"] = {k.lower(): v for k,v in commands.items()} storage.save()
def modify_commands(commands): storage.data["responses"] = {" ".join(k.lower().split()): v for k, v in commands.items()} storage.save() generate_hook()
def modify_commands(commands): log.info("Setting commands to %r" % commands) storage.data["responses"] = {" ".join(k.lower().split()): v for k, v in commands.items()} storage.save() generate_hook()