how to implement Django-Private-Chat in Django 2
Asked Answered
C

1

5

I'm working on a project in which I need to implement private chat for user, so one user can communicate with another user privately, for that purpose I'm using django-private-chat but I'm confused in how to implement this in my application?

Here's what I have done so far:

1): Install websockets 2): Install django-private-chat 3): Add it in INSTALLED_APPS 4): Then add in main urls.py like:

From project.urls.py:

path('', include('jobexpertapp.urls')),
url(r'', include('django_private_chat.urls')),

From myapp.urls.py:

re_path(r'^users/$', views.UserListView.as_view(), name='user_list'),

5): Add a view for that:

class UserListView(LoginRequiredMixin, generic.ListView):
    model = get_user_model()
    # These next two lines tell the view to index lookups by username
    slug_field = 'username'
    slug_url_kwarg = 'username'
    template_name = 'jobexpertapp/users.html'
    login_url = '/login'

6): Then add two templates: (a): users.html (b): dialogs.html

From users.html:

{% extends "base.html" %}
{% block content %}
    <h3>Here you can see users avaliable for chat, excluding yourself (chat with yourself is possible thought):</h3>
    <div class="container">
        {% for user in object_list %}
            {% if user != request.user %}
                <p>{{ user.get_full_name }}</p>
                <a href="{% url 'dialogs_detail' user.username %}" class="btn btn-primary">
                    Chat with {{ user.username }}
                </a>
            {% endif %}
        {% endfor %}
    </div>

{% endblock %}

From dialogs.html:

{# This template is here just to demonstrate how to customize the default one. #}
{# It has none to very few changes #}
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block css %}
{% endblock css %}

{% block content %}
    <input id="owner_username" type="hidden" value="{{ request.user.username }}">
    <div class="container">
        <div class="col-md-3">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">{% trans "Chat list" %}</h3>
                </div>
                <div class="panel-body">
                    <div class="user-list-div">
                        <ul style="list-style-type: none;">
                            {% for dialog in object_list %}
                                <li>
                                    {% if dialog.owner == request.user %}
                                        {% with dialog.opponent.username as username %}
                                            <a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
                                               class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
                                        {% endwith %}
                                    {% else %}
                                        {% with dialog.owner.username as username %}
                                            <a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
                                               class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
                                        {% endwith %}
                                    {% endif %}
                                </li>
                            {% endfor %}
                        </ul>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">
            <div class="well well-lg">
                <div class="row">
                    <div class="col-md-3 col-md-offset-9">
                    <span class="pull-right" hidden id="typing-text">
                        <strong>{{ opponent_username }} {% trans "is typing..." %}</strong>
                    </span>
                    </div>
                    <p>
                        {{ opponent_username }}
                    </p>
                    <p class="text-success" id="online-status" style="display: none">{% trans "Online" %}</p>
                    <p class="text-danger" id="offline-status" style="display: none">{% trans "Offline" %}</p>

                    <div class="messages-container">
                        <div id="messages" class="messages">
                            {% for msg in active_dialog.messages.all %}
                                <div class="row  {% if msg.read %}msg-read{% else %}msg-unread{% endif %} {% if msg.sender != request.user %}opponent{% endif %}"
                                     data-id="{{ msg.id }}">
                                    <p class="{% if msg.sender == request.user %}pull-left{% else %}pull-right{% endif %}">
                                        <span class="username">{{ msg.sender.username }}:</span>
                                        {{ msg.text }}
                                        <span class="timestamp">&ndash; <span
                                                data-livestamp="{{ msg.get_formatted_create_datetime }}">{{ msg.get_formatted_create_datetime }}</span></span>
                                    </p>
                                </div>
                            {% endfor %}
                        </div>
                    </div>
                </div>

                <div class="row">
                    <div class="add-message">
                        <div class="form-group">
                        <textarea id="chat-message" class="form-control message"
                                  placeholder="{% trans 'Write a message' %}"></textarea>
                        </div>

                        <div class="form-group clearfix">
                            <input id="btn-send-message" type="submit" class="btn btn-primary pull-right send-message"
                                   style="margin-left: 10px;" value="{% trans 'Send' %}"/>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

{% block extra_js %}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/scrollmonitor/1.2.0/scrollMonitor.js"
            integrity="sha256-BseZlDlA+yL4qu+Voi82iFa5aaifralQEXIjOjaXgeo=" crossorigin="anonymous"></script>
    <script>
        var base_ws_server_path = "{{ ws_server_path }}";
        $(document).ready(function () {
            var websocket = null;
            var monitor = null;
            function initReadMessageHandler(containerMonitor, elem) {
                var id = $(elem).data('id');
                var elementWatcher = containerMonitor.create(elem);
                elementWatcher.enterViewport(function () {
                    var opponent_username = getOpponnentUsername();
                    var packet = JSON.stringify({
                        type: 'read_message',
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        message_id: id
                    });
                    $(elem).removeClass('msg-unread').addClass('msg-read');
                    websocket.send(packet);
                });
            }
            function initScrollMonitor() {
                var containerElement = $("#messages");
                var containerMonitor = scrollMonitor.createContainer(containerElement);
                $('.msg-unread').each(function (i, elem) {
                    if ($(elem).hasClass('opponent')){
                        initReadMessageHandler(containerMonitor, elem);
                    }
                });
                return containerMonitor
            }
            function getOpponnentUsername() {
                return "{{ opponent_username }}";
            }
            // TODO: Use for adding new dialog
            function addNewUser(packet) {
                $('#user-list').html('');
                packet.value.forEach(function (userInfo) {
                    if (userInfo.username == getUsername()) return;
                    var tmpl = Handlebars.compile($('#user-list-item-template').html());
                    $('#user-list').append(tmpl(userInfo))
                });
            }
            function addNewMessage(packet) {
                var msg_class = "";
                if (packet['sender_name'] == $("#owner_username").val()) {
                    msg_class = "pull-left";
                } else {
                    msg_class = "pull-right";
                }
                var msgElem =
                    $('<div class="row msg-unread" data-id="' + packet.message_id + '">' +
                        '<p class="' + msg_class + '">' +
                        '<span class="username">' + packet['sender_name'] + ': </span>' +
                        packet['message'] +
                        ' <span class="timestamp">&ndash; <span data-livestamp="' + packet['created'] + '"> ' + packet['created'] + '</span></span> ' +
                        '</p> ' +
                        '</div>');
                $('#messages').append(msgElem);
                scrollToLastMessage()
            }
            function scrollToLastMessage() {
                var $msgs = $('#messages');
                $msgs.animate({"scrollTop": $msgs.prop('scrollHeight')})
            }
            function generateMessage(context) {
                var tmpl = Handlebars.compile($('#chat-message-template').html());
                return tmpl({msg: context})
            }
            function setUserOnlineOffline(username, online) {
                var elem = $("#user-" + username);
                if (online) {
                    elem.attr("class", "btn btn-success");
                } else {
                    elem.attr("class", "btn btn-danger");
                }
            }
            function gone_online() {
                $("#offline-status").hide();
                $("#online-status").show();
            }
            function gone_offline() {
                $("#online-status").hide();
                $("#offline-status").show();
            }
            function flash_user_button(username) {
                var btn = $("#user-" + username);
                btn.fadeTo(700, 0.1, function () {
                    $(this).fadeTo(800, 1.0);
                });
            }
            function setupChatWebSocket() {
                var opponent_username = getOpponnentUsername();
                websocket = new WebSocket(base_ws_server_path + '{{ request.session.session_key }}/' + opponent_username);
                websocket.onopen = function (event) {
                    var opponent_username = getOpponnentUsername();
                    var onOnlineCheckPacket = JSON.stringify({
                        type: "check-online",
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username
                        {#                      Sending username because the user needs to know if his opponent is online #}
                    });
                    var onConnectPacket = JSON.stringify({
                        type: "online",
                        session_key: '{{ request.session.session_key }}'
                    });
                    console.log('connected, sending:', onConnectPacket);
                    websocket.send(onConnectPacket);
                    console.log('checking online opponents with:', onOnlineCheckPacket);
                    websocket.send(onOnlineCheckPacket);
                    monitor = initScrollMonitor();
                };
                window.onbeforeunload = function () {
                    var onClosePacket = JSON.stringify({
                        type: "offline",
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        {# Sending username because to let opponnent know that the user went offline #}
                    });
                    console.log('unloading, sending:', onClosePacket);
                    websocket.send(onClosePacket);
                    websocket.close();
                };
                websocket.onmessage = function (event) {
                    var packet;
                    try {
                        packet = JSON.parse(event.data);
                        console.log(packet)
                    } catch (e) {
                        console.log(e);
                    }
                    switch (packet.type) {
                        case "new-dialog":
                            // TODO: add new dialog to dialog_list
                            break;
                        case "user-not-found":
                            // TODO: dispay some kind of an error that the user is not found
                            break;
                        case "gone-online":
                            if (packet.usernames.indexOf(opponent_username) != -1) {
                                gone_online();
                            } else {
                                gone_offline();
                            }
                            for (var i = 0; i < packet.usernames.length; ++i) {
                                setUserOnlineOffline(packet.usernames[i], true);
                            }
                            break;
                        case "gone-offline":
                            if (packet.username == opponent_username) {
                                gone_offline();
                            }
                            setUserOnlineOffline(packet.username, false);
                            break;
                        case "new-message":
                            var username = packet['sender_name'];
                           if (username == opponent_username || username == $("#owner_username").val()){
                                addNewMessage(packet);
                                if (username == opponent_username) {
                                    initReadMessageHandler(monitor, $("div[data-id='" + packet['message_id'] + "']"));
                                }
                            } else {
                                if ($("#user-"+username).length == 0){
                                    var new_button = $(''+
                                        '<a href="/'+ username + '"' +
                                        'id="user-'+username+'" class="btn btn-danger">{% trans "Chat with" %} '+username+'</a>');
                                    $("#user-list-div").find("ul").append()
                                }
                                flash_user_button(username);
                            }
                            break;
                        case "opponent-typing":
                            var typing_elem = $('#typing-text');
                            if (!typing_elem.is(":visible")) {
                                typing_elem.fadeIn(500);
                            } else {
                                typing_elem.stop(true);
                                typing_elem.fadeIn(0);
                            }
                            typing_elem.fadeOut(3000);
                            break;
                        case "opponent-read-message":
                            if (packet['username'] == opponent_username) {
                                $("div[data-id='" + packet['message_id'] + "']").removeClass('msg-unread').addClass('msg-read');
                            }
                            break;
                        default:
                            console.log('error: ', event)
                    }
                }
            }
            function sendMessage(message) {
                var opponent_username = getOpponnentUsername();
                var newMessagePacket = JSON.stringify({
                    type: 'new-message',
                    session_key: '{{ request.session.session_key }}',
                    username: opponent_username,
                    message: message
                });
                websocket.send(newMessagePacket)
            }
            $('#chat-message').keypress(function (e) {
                if (e.which == 13 && this.value) {
                    sendMessage(this.value);
                    this.value = "";
                    return false
                } else {
                    var opponent_username = getOpponnentUsername();
                    var packet = JSON.stringify({
                        type: 'is-typing',
                        session_key: '{{ request.session.session_key }}',
                        username: opponent_username,
                        typing: true
                    });
                    websocket.send(packet);
                }
            });
            $('#btn-send-message').click(function (e) {
                var $chatInput = $('#chat-message');
                var msg = $chatInput.val();
                if (!msg) return;
                sendMessage($chatInput.val());
                $chatInput.val('')
            });
            setupChatWebSocket();
            scrollToLastMessage();
        });
    </script>
{% endblock %}
Calends answered 1/2, 2019 at 14:16 Comment(0)
A
8

You are making it too complex,you don't need to write any view or anything.After You have installed django-private-chat using pip install django-private-chat and added it to installed apps. Add following lines to your settings .py file

CHAT_WS_SERVER_HOST = 'localhost'
CHAT_WS_SERVER_PORT = 5002
CHAT_WS_SERVER_PROTOCOL = 'ws'

Next thing you need to do is all its url to your main project's urls.py. It should look something like this , URLS.py

from django_private_chat import urls as django_private_chat_urls

urlpatterns = [
#other urls of ur project
path('', include('django_private_chat.urls')),
]

Next step is front-end part,hope u have an static directory of your project,if not create an static folder in path same as of your manage.py file of your project. Now copy contents of static folder of django-private-chat from enviroment.It's path would be your_venv_name/lib/pythonX.X/site-packages/django_private_chat/static.Copy all the contents of this static folder to your project's static folder.It should look somehing like this your_project/static/django_private_chat/static

Next thing is your dailog.html page. If you don't have templates folder in your project already created,create one.Then In that templates folder make an 'base.html' file, contents of your base.html file would be something like this: base.html

 <html><head></head><body>
    {% block content %}

    {% endblock %}
    {%block js%}
    {%endblock%}
    {% block extra_js %}{% endblock extra_js %}

</body>

Next in your templates folder,create dailogs.html file,an copy whatever you have written in dailogs.html in your question,just copy this inside the {%block css%}

{{ block.super }}
<link href="{% static 'django_private_chat/css/django_private_chat.css' %}" rel="stylesheet" type="text/css" media="all">

Now in One terminal window runserver and in another terminal windows from same directory of where your runserver is running. Execute

python manage.py run_chat_server

go to browser and login into your django project.then go to http://127.0.0.1:8000/dialogs/

you'll find the users to chat with.

Armendariz answered 3/3, 2019 at 8:8 Comment(5)
I have followed these steps but can't make it work. I am seeing this error in the console. File "/Users/raakesh/Python/venvironment/lib/python3.7/site-packages/websockets/compatibility.py", line 8 asyncio_ensure_future = asyncio.async # Python < 3.5Mcnully
It was fixed. It was an issue with the Python version. I was using 3.7 and so I had to update the websockets library to 7.0 instead od 3.2 which was required for this chat libraryMcnully
@RaakeshKumar please upvote the answer if it has helped youArmendariz
Why do the import: from django_private_chat import urls as django_private_chat_urls ? since you don't use django_private_chat_urls.Fabozzi
How did you solve this problem? I cant get it to workKeim

© 2022 - 2024 — McMap. All rights reserved.