Javascript push notifications with Django & Pusher

I've been running into an increasing need for JS push notifications and have played with a few ways of doing it. Recently ran across Pusher API. They use a combo of HTML5 websockets & flash fallbacks. Much easier than Comet, etc.

But their python library didn't support presence. Here's everything you need to make presence work in Django, including an updated library.

My JS

var pusher = new Pusher('MY_PUBLIC_KEY');
Pusher.channel_auth_endpoint = '/api/pusher/auth/'
var channel = pusher.subscribe('presence-meeting-1')

channel.bind('pusher:subscription_succeeded', function(data) {
  console.log("subscription_succeeded")
  console.log(data)
})

channel.bind('pusher:member_added', function(member) {
  console.log("member added")
  console.log([member.id, member.info]);
})

channel.bind('pusher:member_removed', function(member) {
  console.log("member removed")
  console.log([member.id, member.info]);
})

Added to urls.py dispatcher

(r'^api/pusher/auth/$', 'MY_MODULE.views.pusher_endpoint'),

In settings.py

PUSHER_APP_ID = 'MY_APP_ID'
PUSHER_KEY = 'MY_APP_ID'
PUSHER_SECRET = 'MY_APP_ID'

My endpoint for the callbacks in MY_MODULE

from django.conf import settings
import pusher2 # since i created my own pusher library, called pusher2
import simplejson
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def pusher_endpoint(request):
    channel = request.POST['channel_name']
    socket_id = request.POST['socket_id']
    secret = settings.PUSHER_SECRET
    key = settings.PUSHER_KEY
   
    p = pusher2.Pusher(app_id=settings.PUSHER_APP_ID, key=key, secret=secret)

    json_data_as_string = '{"user_id":%d, "user_info": {"name": "%s"}}' % (request.user.id, request.user)

    signed_presence = p.authenticate(socket_id, channel, json_data_as_string)
   
    obj = {
      'auth': '%s:%s' % (key, signed_presence),
      'channel_data': json_data_as_string
    }

    return HttpResponse(simplejson.dumps(obj), mimetype='application/json')

My updated Pusher python class, with an authenticate method: ( should replace the Pusher class in the the official library, which lacks the auth method. Thanks to Phil from Pusher for steering me in the right direction. )

class Pusher(object):
    def __init__(self, app_id=None, key=None, secret=None, host=None, port=None):
        _globals = globals()
        self.app_id = app_id or _globals['app_id']
        self.key = key or _globals['key']
        self.secret = secret or _globals['secret']
        self.host = host or _globals['host']
        self.port = port or _globals['port']
        self._channels = {}

    def __getitem__(self, key):
        if not self._channels.has_key(key):
            return self._make_channel(key)
        return self._channels[key]

    def _make_channel(self, name):
        self._channels[name] = channel_type(name, self)
        return self._channels[name]

    def authenticate(self, socket_id, channel_name, json_data_as_string = None):
     
      string_to_sign = "%s:%s" % ( socket_id, channel_name )
     
      if json_data_as_string != None:
        string_to_sign += ":%s" % json_data_as_string

      signed = hmac.new(self.secret, string_to_sign, hashlib.sha256).hexdigest()
     
      return signed