Asirra in Django Comments
Following on from my last post, I’ve refactored the way I handle spam. This isn’t live yet because I’ve only completed rudimentary testing (which is all I do really!), but this revised code checks comments against both the Asirra CAPTCHA service and Akismet:
# Imports for Asirra
import urllib
from xml.dom.minidom import parse
# Imports for Akismet
from django.contrib.sites.models import Site
from django.conf import settings
from akismet import Akismet, AkismetError
def check_comment_from_unauthenticated_user(sender, **kwargs):
"""
If a comment being posted is arriving from a member of the public, rather
than an authenticated user, we check the comment against Asirra and Akismet.
If Asirra fails, we reject the comment outright, whereas if Akismet fails
we simply set is_public to False in order to hide the comment, pending a
review from an administrator.
"""
comment = kwargs.get('comment', None)
request = kwargs.get('request', None)
if comment is None or request is None:
return False
if comment.user is None:
# Anonymous user, therefore we'll perform an Asirra and Akismet check.
if not check_asirra(request.POST):
# Asirra failed
return False
if not check_akismet(comment, request.META):
# Akismet failed. It could still be genuine, so we save it - it's
# just not public.
comment.is_public = False
return True
def check_asirra(post):
"""
Checks that the anonymously posted comment passes the Asirra CAPTCHA. Returns
True if Asirra validation passed, and False if it failed. Takes request.POST
as an argument.
"""
if 'Asirra_Ticket' in post:
ticket = post['Asirra_Ticket']
validation_url = ("%s?action=ValidateTicket&ticket=%s" % (settings.ASIRRA_URL, ticket))
validation_stream = urllib.urlopen(validation_url)
dom = parse(validation_stream)
validation_stream.close()
value = dom.getElementsByTagName("Result")[0].childNodes[0].data
if value != "Pass":
return False
else:
return False
return True
def check_akismet(comment, meta):
"""
Checks the comment against Akismet anti-spam, returning True if the comment
is not spam, and False if it is spam. Takes the comment object, and
request.META as arguments.
"""
ak = Akismet(key=settings.AKISMET_API_KEY,
blog_url='http://%s/' % Site.objects.get_current().domain)
try:
if ak.verify_key():
data = {
'user_ip': meta.get('REMOTE_ADDR', '127.0.0.1'),
'user_agent': meta.get('HTTP_USER_AGENT', ''),
'referrer': meta.get('HTTP_REFERER', ''),
'comment_type': 'comment',
'comment_author': comment.user_name.encode('utf-8'),
}
if ak.comment_check(comment.comment.encode('utf-8'), data=data, build_data=True):
# Akismet check failed - comment is spam
return False
except AkismetError:
return False
return True
The above has worked for me in rudimentary testing.
UPDATE - it’s worth noting that since switching to Jekyll as a generator, I no longer use Django’s comments utility so you won’t be able to see the above code in action anymore. I’ll endeavour to clean up my original blog’s code a bit and publish it on github.
Posted: 09 Jan, 2009
Tags:
blog comments powered by Disqus
Tags: