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.contrib.staticfiles',
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
'ircbot',
|
'ircbot',
|
||||||
|
'karma',
|
||||||
'markov',
|
'markov',
|
||||||
'races',
|
'races',
|
||||||
'seen',
|
'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