text: import re
  import json
  import logging
  from channels import Group
  from channels.sessions import channel_session
  from .models import Room
  
  log = logging.getLogger(__name__)
  
  @channel_session
  def ws_connect(message):
      # Extract the room from the message. This expects message.path to be of the
      # form /chat/{label}/, and finds a Room if the message path is applicable,
      # and if the Room exists. Otherwise, bails (meaning this is a some othersort
      # of websocket). So, this is effectively a version of _get_object_or_404.
      try:
          prefix, label = message['path'].decode('ascii').strip('/').split('/')
          if prefix != 'chat':
              log.debug('invalid ws path=%s', message['path'])
              return
          room = Room.objects.get(label=label)
      except ValueError:
          log.debug('invalid ws path=%s', message['path'])
          return
      except Room.DoesNotExist:
          log.debug('ws room does not exist label=%s', label)
          return
  
      log.debug('chat connect room=%s client=%s:%s', 
          room.label, message['client'][0], message['client'][1])
      
      # Need to be explicit about the channel layer so that testability works
      # This may be a FIXME?
      Group('chat-'+label, channel_layer=message.channel_layer).add(message.reply_channel)
  
      message.channel_session['room'] = room.label
  
  @channel_session
  def ws_receive(message):
      # Look up the room from the channel session, bailing if it doesn't exist
      try:
          label = message.channel_session['room']
          room = Room.objects.get(label=label)
      except KeyError:
          log.debug('no room in channel_session')
          return
      except Room.DoesNotExist:
          log.debug('recieved message, buy room does not exist label=%s', label)
          return
  
      # Parse out a chat message from the content text, bailing if it doesn't
      # conform to the expected message format.
      try:
          data = json.loads(message['text'])
      except ValueError:
          log.debug("ws message isn't json text=%s", message['text'])
          return
  
      log.debug("message_keys(type %s): %s", type(data.keys()), data.keys())
      log.debug("message type: %s", data['type'])
  
      if 'type' not in data:
          log.debug("ws message unexpected format data=%s", data)
          return
  
      if data:
          if data['type'] == 'message':
              log.debug('chat message room=%s handle=%s message=%s',
                  room.label, data['handle'], data['message'])
              m = room.messages.create(handle=data['handle'],
                                       message=data['message'])
  
              reply = dict(type='message', **m.as_dict())
              # See above for the note about Group
              Group('chat-'+label, channel_layer=message.channel_layer).send({'text': json.dumps(reply)})
              # Group('chat-'+label, channel_layer=message.channel_layer).send(m.as_dict())
              log.debug("group message sent %s", reply)
  
          elif data['type'] == 'reference':
              log.debug('chat reference room=%s reference=%s',
                        room.label, data['reference'])
              referred_room_label = re.sub(r'[^a-zA-z0-9]+', '-', data['reference'])
              referred_room = Room.objects.get_or_create(label=referred_room_label)[0]
              room.referred_rooms.add(referred_room)  # TODO - DEBUG
              room.save()
              # Notify room member of reference
              reply = {'referred_room': referred_room_label, 'type': 'reference'}
              Group('chat-'+label, channel_layer=message.channel_layer).send({'text': json.dumps(reply)})
              log.debug("group reference sent %s", reply)
  
  
  @channel_session
  def ws_disconnect(message):
      try:
          label = message.channel_session['room']
          room = Room.objects.get(label=label)
          Group('chat-'+label, channel_layer=message.channel_layer).discard(message.reply_channel)
      except (KeyError, Room.DoesNotExist):
          pass
  