dr.botzo/karma/ircplugin.py

185 lines
8.7 KiB
Python

"""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