From 0641e1c062d79be5a884fb0e97fcdc04509495de Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Thu, 18 Jun 2015 23:57:43 -0500 Subject: [PATCH] dispatch: port to django REST framework this moves the dispatcher functionality that used to be in the old ircbot directly to django REST framework, which is more robust and allows for cooler stuff down the road. this still retains the ability to have the bot privmsg, that still happens over the XML-RPC interface, this is just a more convenient frontend to that --- dr_botzo/dispatch/__init__.py | 0 dr_botzo/dispatch/admin.py | 8 +++ dr_botzo/dispatch/migrations/0001_initial.py | 22 ++++++ dr_botzo/dispatch/migrations/__init__.py | 0 dr_botzo/dispatch/models.py | 30 ++++++++ dr_botzo/dispatch/serializers.py | 17 +++++ dr_botzo/dispatch/urls.py | 12 ++++ dr_botzo/dispatch/views.py | 72 ++++++++++++++++++++ dr_botzo/dr_botzo/settings.py | 11 +++ dr_botzo/dr_botzo/urls.py | 1 + requirements.txt | 5 +- 11 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 dr_botzo/dispatch/__init__.py create mode 100644 dr_botzo/dispatch/admin.py create mode 100644 dr_botzo/dispatch/migrations/0001_initial.py create mode 100644 dr_botzo/dispatch/migrations/__init__.py create mode 100644 dr_botzo/dispatch/models.py create mode 100644 dr_botzo/dispatch/serializers.py create mode 100644 dr_botzo/dispatch/urls.py create mode 100644 dr_botzo/dispatch/views.py diff --git a/dr_botzo/dispatch/__init__.py b/dr_botzo/dispatch/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dr_botzo/dispatch/admin.py b/dr_botzo/dispatch/admin.py new file mode 100644 index 0000000..21c55ed --- /dev/null +++ b/dr_botzo/dispatch/admin.py @@ -0,0 +1,8 @@ +"""Manage dispatch models in the admin interface.""" + +from django.contrib import admin + +from dispatch.models import Dispatcher + + +admin.site.register(Dispatcher) diff --git a/dr_botzo/dispatch/migrations/0001_initial.py b/dr_botzo/dispatch/migrations/0001_initial.py new file mode 100644 index 0000000..2725b01 --- /dev/null +++ b/dr_botzo/dispatch/migrations/0001_initial.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Dispatcher', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('key', models.CharField(unique=True, max_length=16)), + ('type', models.CharField(max_length=16, choices=[(b'privmsg', b'IRC privmsg'), (b'file', b'Write to file')])), + ('destination', models.CharField(max_length=200)), + ], + ), + ] diff --git a/dr_botzo/dispatch/migrations/__init__.py b/dr_botzo/dispatch/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dr_botzo/dispatch/models.py b/dr_botzo/dispatch/models.py new file mode 100644 index 0000000..919da0f --- /dev/null +++ b/dr_botzo/dispatch/models.py @@ -0,0 +1,30 @@ +"""Track dispatcher configurations.""" + +import logging + +from django.db import models + + +log = logging.getLogger('dispatch.models') + + +class Dispatcher(models.Model): + + """Handle incoming API requests and do something with them.""" + + PRIVMSG_TYPE = 'privmsg' + FILE_TYPE = 'file' + + TYPE_CHOICES = ( + (PRIVMSG_TYPE, "IRC privmsg"), + (FILE_TYPE, "Write to file"), + ) + + key = models.CharField(max_length=16, unique=True) + type = models.CharField(max_length=16, choices=TYPE_CHOICES) + destination = models.CharField(max_length=200) + + def __unicode__(self): + """String representation.""" + + return u"{0:s} -> {1:s}".format(self.key, self.destination) diff --git a/dr_botzo/dispatch/serializers.py b/dr_botzo/dispatch/serializers.py new file mode 100644 index 0000000..f14db08 --- /dev/null +++ b/dr_botzo/dispatch/serializers.py @@ -0,0 +1,17 @@ +"""Serializers for the dispatcher API objects.""" + +from rest_framework import serializers + +from dispatch.models import Dispatcher + + +class DispatcherSerializer(serializers.ModelSerializer): + + class Meta: + model = Dispatcher + fields = ('id', 'key', 'type', 'destination') + + +class DispatchMessageSerializer(serializers.Serializer): + + message = serializers.CharField() diff --git a/dr_botzo/dispatch/urls.py b/dr_botzo/dispatch/urls.py new file mode 100644 index 0000000..58c6a79 --- /dev/null +++ b/dr_botzo/dispatch/urls.py @@ -0,0 +1,12 @@ +"""URL patterns for the dispatcher API.""" + +from django.conf.urls import patterns, url + +from dispatch.views import DispatchMessage, DispatcherList, DispatcherDetail + + +urlpatterns = patterns('dispatch.views', + url(r'^api/dispatchers/$', DispatcherList.as_view(), name='dispatch_api_dispatchers'), + url(r'^api/dispatchers/(?P[0-9]+)/$', DispatcherDetail.as_view(), name='dispatch_api_dispatcher_detail'), + url(r'^api/dispatchers/(?P[0-9]+)/message$', DispatchMessage.as_view(), name='dispatch_api_dispatch_message'), +) diff --git a/dr_botzo/dispatch/views.py b/dr_botzo/dispatch/views.py new file mode 100644 index 0000000..bab2e0c --- /dev/null +++ b/dr_botzo/dispatch/views.py @@ -0,0 +1,72 @@ +"""Handle dispatcher API requests.""" + +from __future__ import unicode_literals + +import logging +import os +import xmlrpclib + +from django.conf import settings +from django.http import Http404 +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import generics, status + +from dispatch.models import Dispatcher +from dispatch.serializers import DispatchMessageSerializer, DispatcherSerializer + + +log = logging.getLogger('dispatch.views') + + +class DispatcherList(generics.ListAPIView): + + """List all dispatchers.""" + + queryset = Dispatcher.objects.all() + serializer_class = DispatcherSerializer + + +class DispatcherDetail(generics.RetrieveAPIView): + + """Detail the given dispatcher.""" + + queryset = Dispatcher.objects.all() + serializer_class = DispatcherSerializer + + +class DispatchMessage(APIView): + + """Send a message to the given dispatcher.""" + + queryset = Dispatcher.objects.none() + + def get_object(self, pk): + try: + return Dispatcher.objects.get(pk=pk) + except Dispatcher.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + dispatcher = self.get_object(pk) + return Response({'message': ""}) + + def post(self, request, pk, format=None): + dispatcher = self.get_object(pk) + serializer = DispatchMessageSerializer(data=request.data) + if serializer.is_valid(): + if dispatcher.type == Dispatcher.PRIVMSG_TYPE: + bot_url = 'http://{0:s}:{1:d}/'.format(settings.IRCBOT_XMLRPC_HOST, settings.IRCBOT_XMLRPC_PORT) + bot = xmlrpclib.ServerProxy(bot_url) + log.debug("sending '%s' to channel %s", serializer.data['message'], dispatcher.destination) + bot.privmsg(dispatcher.destination, serializer.data['message']) + return Response({'status': "OK"}) + elif dispatcher.type == Dispatcher.FILE_TYPE: + filename = os.path.abspath(dispatcher.destination) + log.debug("sending '%s' to file %s", serializer.data['message'], filename) + with open(filename, 'w') as f: + f.write(serializer.data['message']) + f.write('\n') + return Response({'status': "OK"}) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/dr_botzo/dr_botzo/settings.py b/dr_botzo/dr_botzo/settings.py index d857502..6999a62 100644 --- a/dr_botzo/dr_botzo/settings.py +++ b/dr_botzo/dr_botzo/settings.py @@ -37,7 +37,9 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'django_extensions', 'adminplus', + 'rest_framework', 'countdown', + 'dispatch', 'facts', 'ircbot', 'karma', @@ -121,6 +123,15 @@ STATICFILES_DIRS = ( ) +REST_FRAMEWORK = { + # Use Django's standard `django.contrib.auth` permissions, + # or allow read-only access for unauthenticated users. + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' + ] +} + + # IRC bot stuff # tuple of hostname, port number, and password (or None) diff --git a/dr_botzo/dr_botzo/urls.py b/dr_botzo/dr_botzo/urls.py index baef8e8..6196d80 100644 --- a/dr_botzo/dr_botzo/urls.py +++ b/dr_botzo/dr_botzo/urls.py @@ -9,6 +9,7 @@ admin.autodiscover() urlpatterns = patterns('', url(r'^$', 'dr_botzo.views.home', name='home'), + url(r'^dispatch/', include('dispatch.urls')), url(r'^markov/', include('markov.urls')), url(r'^races/', include('races.urls')), diff --git a/requirements.txt b/requirements.txt index c760540..09e2f26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ Django==1.8.1 +django-adminplus==0.3 django-extensions==1.5.3 +django-filter==0.10.0 +djangorestframework==3.1.3 httplib2==0.7.4 inflect==0.2.5 irc==12.1.4 @@ -13,6 +16,7 @@ jaraco.logging==1.2 jaraco.text==1.3 logilab-astng==0.24.0 logilab-common==0.58.1 +Markdown==2.6.2 more-itertools==2.2 MySQL-python==1.2.3 oauth2==1.5.211 @@ -29,4 +33,3 @@ tempora==1.3 twython==3.0.0 yg.lockfile==2.0 zc.lockfile==1.1.0 -django-adminplus==0.3