185 lines
8.7 KiB
Python
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
|