"""Karma hooks for the IRC bot.""" import logging import re import irc.client from django.conf import settings from django.db.models import Count, Sum from ircbot.lib import Plugin from karma.models import KarmaKey, KarmaLogEntry log = logging.getLogger('karma.ircplugin') class Karma(Plugin): """Track karma and report on it.""" def start(self): """Set up the handlers.""" self.connection.add_global_handler('pubmsg', self.handle_chatter, -20) self.connection.add_global_handler('privmsg', self.handle_chatter, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], (r'^!karma\s+keyreport\s+(.*)'), self.handle_keyreport, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!karma\s+rank\s+(.*)$', self.handle_rank, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], (r'^!karma\s+report\s+(highest|lowest|positive|negative' r'|top|opinionated)'), self.handle_report, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!karma\s+stats\s+([\S]+)', self.handle_stats, -20) super(Karma, self).start() def stop(self): """Tear down handlers.""" self.connection.remove_global_handler('pubmsg', self.handle_chatter) self.connection.remove_global_handler('privmsg', self.handle_chatter) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_keyreport) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_rank) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_report) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_stats) super(Karma, self).stop() @staticmethod def handle_chatter(connection, event): """Watch karma from IRC chatter.""" what = event.arguments[0].lower() karma_pattern = r'(?:\((.+?)\)|(\S+))(\+\+|--|\+-|-\+)(\s+|$)' where = event.target if where in settings.KARMA_IGNORE_CHATTER_TARGETS: log.debug("ignoring chatter in {0:s}".format(where)) return # check the line for karma log.debug("searching '%s' for karma", what) matches = re.findall(karma_pattern, what, re.IGNORECASE) for match in matches: key = match[0] if match[0] else match[1] value = match[2] log.debug("key length: %d", len(key)) if len(key) > KarmaKey._meta.get_field('key').max_length: log.warning("given a key longer than %d, ignoring", KarmaKey._meta.get_field('key').max_length) return karma_key, c = KarmaKey.objects.get_or_create(key=key) if value == '++': KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source) elif value == '--': KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source) elif value == '+-': KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source) KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source) elif value == '-+': KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source) KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source) def handle_rank(self, connection, event, match): """Report on the rank of a karma item.""" where = event.target if where in settings.KARMA_IGNORE_COMMAND_TARGETS: log.debug("ignoring command in {0:s}".format(where)) return key = match.group(1).lower().rstrip() try: karma_key = KarmaKey.objects.get(key=key) return self.bot.reply(event, "{0:s} has {1:d} points of karma (rank {2:d})".format(karma_key.key, karma_key.score(), karma_key.rank())) except KarmaKey.DoesNotExist: return self.bot.reply(event, "i have not seen any karma for {0:s}".format(match.group(1))) def handle_keyreport(self, connection, event, match): """Provide report on a karma key.""" key = match.group(1).lower().rstrip() try: karma_key = KarmaKey.objects.get(key=key) karmaers = KarmaLogEntry.objects.filter(key=karma_key) karmaers = karmaers.values('nickmask').annotate(Sum('delta')).annotate(Count('delta')).order_by('-delta__count') karmaers_list = [f"{irc.client.NickMask(x['nickmask']).nick} ({x['delta__count']}, {'+' if x['delta__sum'] >= 0 else ''}{x['delta__sum']})" for x in karmaers] karmaers_list_str = ", ".join(karmaers_list[:10]) return self.bot.reply(event, f"most opinionated on {key}: {karmaers_list_str}") except KarmaKey.DoesNotExist: return self.bot.reply(event, "i have not seen any karma for {0:s}".format(match.group(1))) def handle_report(self, connection, event, match): """Provide some karma reports.""" where = event.target if where in settings.KARMA_IGNORE_COMMAND_TARGETS: log.debug("ignoring command in {0:s}".format(where)) return report = match.group(1).lower() if report == 'highest': sorted_keys = KarmaKey.objects.ranked_scored_order() msg = "top 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]])) return self.bot.reply(event, msg) elif report == 'lowest': sorted_keys = KarmaKey.objects.ranked_scored_reverse_order() msg = "bottom 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]])) return self.bot.reply(event, msg) elif report == 'positive': karmaers = KarmaLogEntry.objects.optimists() karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick, x['karma_outlook']) for x in karmaers[:5]]) msg = "top 5 optimists: {0:s}".format(karmaer_list) return self.bot.reply(event, msg) elif report == 'negative': karmaers = KarmaLogEntry.objects.pessimists() karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick, x['karma_outlook']) for x in karmaers[:5]]) msg = "top 5 pessimists: {0:s}".format(karmaer_list) return self.bot.reply(event, msg) elif report == 'top' or report == 'opinionated': karmaers = KarmaLogEntry.objects.most_opinionated() karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick, x['karma_count']) for x in karmaers[:5]]) msg = "top 5 opinionated users: {0:s}".format(karmaer_list) return self.bot.reply(event, msg) def handle_stats(self, connection, event, match): """Provide stats on a karma user.""" where = event.target if where in settings.KARMA_IGNORE_COMMAND_TARGETS: log.debug("ignoring command in {0:s}".format(where)) return karmaer = match.group(1) log_entries = KarmaLogEntry.objects.filter(nickmask=karmaer) if len(log_entries) == 0: # fallback, try to match the nick part of nickmask log_entries = KarmaLogEntry.objects.filter(nickmask__startswith='{0:s}!'.format(karmaer)) if len(log_entries) == 0: return self.bot.reply(event, "karma user {0:s} not found".format(karmaer)) total_karma = log_entries.count() positive_karma = log_entries.filter(delta__gt=0).count() negative_karma = log_entries.filter(delta__lt=0).count() msg = ("{0:s} has given {1:d} postive karma and {2:d} negative karma, for a total of {3:d} karma" "".format(karmaer, positive_karma, negative_karma, total_karma)) return self.bot.reply(event, msg) plugin = Karma