def post(self, request, *args, **kwargs):
        """
        Returns a token identifying the user in Centrifugo.
        """

        current_timestamp = "%.0f" % time.time()
        user_id_str = u"{0}".format(request.user.id)
        token = generate_token(settings.CENTRIFUGE_SECRET, user_id_str, "{0}".format(current_timestamp), info="")

        # we get all the channels to which the user can subscribe
        participant = Participant.objects.get(id=request.user.id)

        # we use the threads as channels ids
        channels = []
        for thread in Thread.managers.get_threads_where_participant_is_active(participant_id=participant.id):
            channels.append(
                build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id, thread.participants.all())
            )

        # we also have a channel to alert us about new threads
        threads_channel = build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, request.user.id, [request.user.id])  # he is the only one to have access to the channel
        channels.append(threads_channel)

        # we return the information
        to_return = {
            'user': user_id_str,
            'timestamp': current_timestamp,
            'token': token,
            'connection_url': "{0}connection/".format(settings.CENTRIFUGE_ADDRESS),
            'channels': channels,
            'debug': settings.DEBUG,
        }

        return HttpResponse(json.dumps(to_return), content_type='application/json; charset=utf-8')
def publish_message_to_centrifugo(sender, instance, created, **kwargs):
    """ Publishes each saved message to Centrifugo. """
    if created is True:
        client = Client(
            "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")),
            getattr(settings, "CENTRIFUGE_SECRET"))
        # we ensure the client is still in the thread (he may have left or have been removed)
        active_participants = [
            participation.participant.id
            for participation in Participation.objects.filter(
                thread=instance.thread, date_left__isnull=True).select_related(
                    'participant')
        ]
        client.publish(
            build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE,
                          instance.thread.id, active_participants),
            {
                "id": instance.id,
                "body": instance.body,
                "sender": instance.sender.id,
                "thread": instance.thread.id,
                "sent_at": str(instance.sent_at),
                "is_notification":
                True,  # ATTENTION: check against sender too to be sure to not notify him his message
            })
 def test_token(self):
     # an unauthenticated client will get no token
     response = self.client_unauthenticated.post(reverse('rest_messaging_centrifugo:authentication'), data={})
     self.assertEqual(302, response.status_code)
     # an authenticated client will get a token
     response = self.client_authenticated.post(reverse('rest_messaging_centrifugo:authentication'), data={})
     self.assertEqual(200, response.status_code)
     content = json.loads(compat_json_resp_content(response.content))
     self.assertEqual(int(content["user"]), self.user.id)
     self.assertTrue(content["timestamp"])
     self.assertTrue(content["token"])
     channels = [build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id, thread.participants.all()) for thread in [self.thread1, self.thread2]] +\
         [build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, self.user.id, [self.user.id])]
     self.assertEqual(set(content["channels"]), set(channels))
     # get method does not work
     response = self.client_authenticated.get(reverse('rest_messaging_centrifugo:authentication'))
     self.assertEqual(405, response.status_code)
    def post(self, request, *args, **kwargs):
        """
        Returns a token identifying the user in Centrifugo.
        """

        current_timestamp = "%.0f" % time.time()
        user_id_str = u"{0}".format(request.user.id)
        token = generate_token(settings.CENTRIFUGE_SECRET,
                               user_id_str,
                               "{0}".format(current_timestamp),
                               info="")

        # we get all the channels to which the user can subscribe
        participant = Participant.objects.get(id=request.user.id)

        # we use the threads as channels ids
        channels = []
        for thread in Thread.managers.get_threads_where_participant_is_active(
                participant_id=participant.id):
            channels.append(
                build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id,
                              thread.participants.all()))

        # we also have a channel to alert us about new threads
        threads_channel = build_channel(
            settings.CENTRIFUGO_THREAD_NAMESPACE, request.user.id,
            [request.user.id
             ])  # he is the only one to have access to the channel
        channels.append(threads_channel)

        # we return the information
        to_return = {
            'user': user_id_str,
            'timestamp': current_timestamp,
            'token': token,
            'connection_url':
            "{0}connection/".format(settings.CENTRIFUGE_ADDRESS),
            'channels': channels,
            'debug': settings.DEBUG,
        }

        return HttpResponse(json.dumps(to_return),
                            content_type='application/json; charset=utf-8')
def publish_participation_to_thread(sender, instance, created, **kwargs):
    """ Warns users everytime a thread including them is published. This is done via channel subscription.  """
    if kwargs.get("created_and_add_participants") is True:
        request_participant_id = kwargs.get("request_participant_id")
        if request_participant_id is not None:
            client = Client(
                "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET")
            )
            active_participants = [
                participation.participant
                for participation in Participation.objects.filter(
                    thread=instance, date_left__isnull=True
                ).select_related("participant")
            ]
            for participant in active_participants:
                client.publish(
                    build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, participant.id, [participant.id]),
                    {
                        "message_channel_to_connect_to": build_channel(
                            settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.id, [p.id for p in active_participants]
                        )
                    },
                )
def publish_participation_to_thread(sender, instance, created, **kwargs):
    """ Warns users everytime a thread including them is published. This is done via channel subscription.  """
    if kwargs.get('created_and_add_participants') is True:
        request_participant_id = kwargs.get('request_participant_id')
        if request_participant_id is not None:
            client = Client(
                "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")),
                getattr(settings, "CENTRIFUGE_SECRET"))
            active_participants = [
                participation.participant
                for participation in Participation.objects.filter(
                    thread=instance, date_left__isnull=True).select_related(
                        'participant')
            ]
            for participant in active_participants:
                client.publish(
                    build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE,
                                  participant.id, [participant.id]),
                    {
                        "message_channel_to_connect_to":
                        build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE,
                                      instance.id,
                                      [p.id for p in active_participants])
                    })
def publish_message_to_centrifugo(sender, instance, created, **kwargs):
    """ Publishes each saved message to Centrifugo. """
    if created is True:
        client = Client(
            "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET")
        )
        # we ensure the client is still in the thread (he may have left or have been removed)
        active_participants = [
            participation.participant.id
            for participation in Participation.objects.filter(
                thread=instance.thread, date_left__isnull=True
            ).select_related("participant")
        ]
        client.publish(
            build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.thread.id, active_participants),
            {
                "id": instance.id,
                "body": instance.body,
                "sender": instance.sender.id,
                "thread": instance.thread.id,
                "sent_at": str(instance.sent_at),
                "is_notification": True,  # ATTENTION: check against sender too to be sure to not notify him his message
            },
        )
 def test_build_channel(self):
     namespace = "message"
     name = 1
     ids = [1, 2, 3]
     built = build_channel(namespace=namespace, name=name, user_ids=ids)
     self.assertEqual(built, "message:1#1,2,3".format(namespace, name, ids))
 def test_integration(self):
     # we hit whatever view just to set the cookie
     self.selenium.get(self.live_server_url + reverse('dummy'))
     self.selenium.add_cookie(self.cookie)
     self.selenium.refresh()
     # we create threads which call signals telling centrifugo to connect
     self.thread1 = Thread.managers.get_or_create_thread(self.request, "The #1 Thread", self.participant1.id, self.participant2.id)
     self.thread2 = Thread.managers.get_or_create_thread(self.request, "The #2 Thread", self.participant1.id, self.participant3.id)
     # the following conversation does not include the current user, we do not want it on the screen!
     self.thread_unrelated = Thread.objects.create(name="The unrelated Thread")  # the conversation does not involve the current user, we do not want it on the screen!
     self.participationU1 = Participation.objects.create(participant=self.participant2, thread=self.thread_unrelated)
     self.participationU2 = Participation.objects.create(participant=self.participant3, thread=self.thread_unrelated)
     # we load the index page which contains the logic (in javascript)
     self.selenium.get(self.live_server_url + reverse('index'))
     # we wait a little bit
     time.sleep(4)
     # we create a message
     # this will trigger a publishing signal in django-rest-messaging-centrifugo
     body11 = "hi #11"
     body12 = "hi #12"
     body21 = "hi #21"
     body22 = "hi #22"
     bodyU1 = "We do not want to see this! #1"
     bodyU2 = "We do not want to see this! #2"
     m11 = Message.objects.create(sender=self.participant1, thread=self.thread1, body=body11)
     m12 = Message.objects.create(sender=self.participant2, thread=self.thread1, body=body12)
     m21 = Message.objects.create(sender=self.participant3, thread=self.thread2, body=body21)
     m22 = Message.objects.create(sender=self.participant1, thread=self.thread2, body=body22)
     mU1 = Message.objects.create(sender=self.participant2, thread=self.thread_unrelated, body=bodyU1)
     mU2 = Message.objects.create(sender=self.participant3, thread=self.thread_unrelated, body=bodyU2)
     # the channels are private
     # this means that Centrifugo will check the users ids to know if the user may connect
     # if we query a private channel the user does not belong to, we never see the message
     client = Client("{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET"))
     forbidden_message = "Message forbidden"
     client.publish(
         build_channel(namespace=settings.CENTRIFUGO_MESSAGE_NAMESPACE, name=self.thread_unrelated.id, user_ids=[p.id for p in self.thread_unrelated.participants.all()]),
         forbidden_message
     )
     # we wait a little bit
     time.sleep(4)
     # now the messages should be displayed
     m11 = self.selenium.find_element_by_id('message__{0}'.format(m11.id))
     m12 = self.selenium.find_element_by_id('message__{0}'.format(m12.id))
     m21 = self.selenium.find_element_by_id('message__{0}'.format(m21.id))
     m22 = self.selenium.find_element_by_id('message__{0}'.format(m22.id))
     self.assertTrue(body11 in m11.text)
     self.assertTrue(body12 in m12.text)
     self.assertTrue(body21 in m21.text)
     self.assertTrue(body22 in m22.text)
     # the following ensures we get the new threads created during the connection
     self.thread4 = Thread.managers.get_or_create_thread(self.request, "The #4 Thread", self.participant4.id, self.participant1.id)
     time.sleep(4)
     message_channel_to_connect_to = 'messages:4#4,1'.format(self.thread4.id, self.thread4.participants.all()[0].id, self.thread4.participants.all()[1].id)
     thread_messages = self.selenium.find_element_by_id('thread__{0}'.format(message_channel_to_connect_to))
     self.assertTrue(message_channel_to_connect_to in thread_messages.text)
     # we should not find the unrelated messages
     self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU1.id))
     self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU2.id))
     self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU2.id))
     self.assertEqual([], self.selenium.find_elements_by_xpath("//*[contains(text(), '{0}')]".format(forbidden_message)))
 def test_build_channel(self):
     namespace = "message"
     name = 1
     ids = [1, 2, 3]
     built = build_channel(namespace=namespace, name=name, user_ids=ids)
     self.assertEqual(built, "message:1#1,2,3".format(namespace, name, ids))