karma: app for managing karma, aka ++s and --s
this is a djangoification of the old (really old, actually) karma stuff written by mike bloy. functionality should be the same, and might be a bit faster through the ORM now
This commit is contained in:
parent
2fbd76dba6
commit
062348ef99
@ -37,6 +37,7 @@ INSTALLED_APPS = (
|
||||
'django.contrib.staticfiles',
|
||||
'django_extensions',
|
||||
'ircbot',
|
||||
'karma',
|
||||
'markov',
|
||||
'races',
|
||||
'seen',
|
||||
|
0
dr_botzo/karma/__init__.py
Normal file
0
dr_botzo/karma/__init__.py
Normal file
9
dr_botzo/karma/admin.py
Normal file
9
dr_botzo/karma/admin.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""Manage karma models in the admin interface."""
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from karma.models import KarmaKey, KarmaLogEntry
|
||||
|
||||
|
||||
admin.site.register(KarmaKey)
|
||||
admin.site.register(KarmaLogEntry)
|
139
dr_botzo/karma/ircplugin.py
Normal file
139
dr_botzo/karma/ircplugin.py
Normal file
@ -0,0 +1,139 @@
|
||||
"""Karma hooks for the IRC bot."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
import irc.client
|
||||
|
||||
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+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_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+|$)'
|
||||
|
||||
# check the line for karma
|
||||
log.debug(u"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]
|
||||
|
||||
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."""
|
||||
|
||||
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_report(self, connection, event, match):
|
||||
"""Provide some karma reports."""
|
||||
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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."""
|
||||
|
||||
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
|
30
dr_botzo/karma/migrations/0001_initial.py
Normal file
30
dr_botzo/karma/migrations/0001_initial.py
Normal file
@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KarmaKey',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('key', models.CharField(unique=True, max_length=200)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='KarmaLogEntry',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('delta', models.SmallIntegerField()),
|
||||
('nickmask', models.CharField(default='', max_length=200, blank=True)),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('key', models.ForeignKey(to='karma.KarmaKey')),
|
||||
],
|
||||
),
|
||||
]
|
18
dr_botzo/karma/migrations/0002_auto_20150519_2156.py
Normal file
18
dr_botzo/karma/migrations/0002_auto_20150519_2156.py
Normal file
@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('karma', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='karmalogentry',
|
||||
options={'verbose_name_plural': 'karma log entries'},
|
||||
),
|
||||
]
|
0
dr_botzo/karma/migrations/__init__.py
Normal file
0
dr_botzo/karma/migrations/__init__.py
Normal file
95
dr_botzo/karma/models.py
Normal file
95
dr_botzo/karma/models.py
Normal file
@ -0,0 +1,95 @@
|
||||
"""Karma logging models."""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import pytz
|
||||
|
||||
from irc.client import NickMask
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
|
||||
log = logging.getLogger('karma.models')
|
||||
|
||||
|
||||
class KarmaKeyManager(models.Manager):
|
||||
|
||||
"""Manage handy queries for KarmaKey."""
|
||||
|
||||
def ranked_scored_order(self):
|
||||
keys = self.annotate(karma_score=models.Sum('karmalogentry__delta')).order_by('-karma_score')
|
||||
return keys
|
||||
|
||||
def ranked_scored_reverse_order(self):
|
||||
keys = self.annotate(karma_score=models.Sum('karmalogentry__delta')).order_by('karma_score')
|
||||
return keys
|
||||
|
||||
|
||||
class KarmaKey(models.Model):
|
||||
|
||||
"""Track a thing being karmaed."""
|
||||
|
||||
key = models.CharField(max_length=200, unique=True)
|
||||
|
||||
objects = KarmaKeyManager()
|
||||
|
||||
def __unicode__(self):
|
||||
"""String representation."""
|
||||
|
||||
return "{0:s} ({1:d})".format(self.key, self.score())
|
||||
|
||||
def score(self):
|
||||
"""Determine the score for this karma entry."""
|
||||
|
||||
return KarmaLogEntry.objects.filter(key=self).aggregate(models.Sum('delta'))['delta__sum']
|
||||
|
||||
def rank(self):
|
||||
"""Determine the rank of this karma entry relative to the others."""
|
||||
|
||||
sorted_keys = KarmaKey.objects.ranked_scored_order()
|
||||
for rank, key in enumerate(sorted_keys):
|
||||
if key == self:
|
||||
return rank+1
|
||||
return None
|
||||
|
||||
|
||||
class KarmaLogEntryManager(models.Manager):
|
||||
|
||||
"""Manage handy queries for KarmaLogEntry."""
|
||||
|
||||
def optimists(self):
|
||||
karmaers = self.values('nickmask').annotate(karma_outlook=models.Sum('delta')).order_by('-karma_outlook')
|
||||
return karmaers
|
||||
|
||||
def pessimists(self):
|
||||
karmaers = self.values('nickmask').annotate(karma_outlook=models.Sum('delta')).order_by('karma_outlook')
|
||||
return karmaers
|
||||
|
||||
def most_opinionated(self):
|
||||
karmaers = self.values('nickmask').annotate(karma_count=models.Count('delta')).order_by('-karma_count')
|
||||
return karmaers
|
||||
|
||||
|
||||
class KarmaLogEntry(models.Model):
|
||||
|
||||
"""Track each karma increment/decrement."""
|
||||
|
||||
key = models.ForeignKey('KarmaKey')
|
||||
delta = models.SmallIntegerField()
|
||||
nickmask = models.CharField(max_length=200, default='', blank=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
objects = KarmaLogEntryManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'karma log entries'
|
||||
|
||||
def __unicode__(self):
|
||||
"""String representation."""
|
||||
|
||||
tz = pytz.timezone(settings.TIME_ZONE)
|
||||
return "{0:s}{1:s} @ {2:s} by {3:s}".format(self.key.key, '++' if self.delta > 0 else '--',
|
||||
self.created.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S %Z'),
|
||||
NickMask(self.nickmask).nick)
|
Loading…
Reference in New Issue
Block a user