integrating Django-postman

I am having a heck of a time getting a notification from Django-postman to a websocket so the other user will know when a message has arrived. I have tried numerous attempts and still am having issues. My websocket is running but I cannot get the message to appear. Any help would be appreciated

If you would like assistance with this, we’ll need to see the relevant code. It would also help if you provided a description of how you’re intending this to work (technically).

thanks…i am fairly new to this, so if you will bear with me. What parts of the code do you need to see?

My overall project is a multi-app, one of which is Django-Postman. as you know it is a simple in-app project. What i am attempting to have happen, is when one of my users sends a message from the postman app, the receiver gets a toast-like message on screen, showing a simply message like “bob sent a message” or “harry replied to your message”. My entire project is on a closed intranet, with extremely limited access… so i am having to develop this and switch back and forth between networks…

The initial code we’d need to see then is whatever code you’ve written that, when you send a message through Django-postman, also sends a message to the channels layer to provide this notification.

this is from my views.py:

channel_layer = get_channel_layer()

def send_notification(message):
 # This will print the message to the console
    print(f"Sending message: {message}")

    # This will send the message to the client.
    async_to_sync(channel_layer.send)('notifications', {
        'type': 'notification.message',
        'message': message
    })

here is my consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class NotificationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass
    
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        await self.send(text_data=json.dumps({
            'message': message
        }))

and the script from base.html for starting the websocket:


<script>
    var socket = new WebSocket('ws://localhost:8600/ws/notifications/');

    socket.onmessage = function(event) {
     var data = JSON.parse(event.data);
        console.log('Received message:', message);
    };
    socket.onopen = function(event) {
        console.log('WebSocket connection opened.');
    };

    socket.onclose = function(event) {
        console.log('WebSocket connection closed.');
    };

    
</script>

This is attempting to send to a channel named “notifications”.

Unless you have a worker process associated with that name, there’s no place to deliver the message to.

If you’re looking to send a message to a specific user, you need to “locate” that user.

The method that I use, is that every user logging on joins a group named user_<username>. (e.g. user_ken).

Any other channels client can send a message to me by sending a message to the group user_ken.
Example from what you have above:

def send_notification(message):
 # This will print the message to the console
    print(f"Sending message: {message}")

    # assume that the target username is in `message.username`
    group_name = f'user_{message.username}'
    # This will send the message to the client.
    async_to_sync(channel_layer.group_send)(group_name, {
        'type': 'notification.message',
        'message': message
    })

Otherwise, you would need to know the precise channel name for that user in order to direct the message to that user.

So how would i setup what your describing? my friends call me a ducktape programmer and it is bearing true!! Thanks for helping… i can see some of what your describing but i do not understand how to set the channel_layer_group.

Your consumer would have each connecting user create their group. See the docs at Channel Layers — Channels 4.0.0 documentation.

In this particular case, each user, when they connect their websocket, would be joining their individual group.

will so what i can… before i go, from your experience, is there a better way of doing what i am attempting with Django-postman? if so i may need to go down that trail. Again thanks

Sorry, I’ve never used Django-postman and have never had the need for the type of messaging it supports.

Thanks and God bless…you have made it alot easier…

hey ken…i am back. i have made some modifications and am stuck on the previous conversations. i have figured out how to do some of it, but then again what is supposed to work is not. i have finally gotten websockets to talk and send the message across from Django-Postman, but i still cannot get the notification to appear on the recipients screen. I get my toast saying message sent, but the other user should get one that shows they are receiving a message. here is my base.html where all the code for websockets are and then i will place my consumers.py at the end. any suggestions?

<!---<META http-equiv="REFRESH" content="10; url=http://localhost:8600/postman/inbox/">--->
<html lang="en">
{% load static %}
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %} CAMAHub - DjangoTest{% endblock %} </title>

    <link rel="icon" href="{% static 'images/cama_logo_shield.png' %}">
    
    <link rel="stylesheet" type="text/css" href="{% static 'bs/css/bootstrap.min.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'fa/css/all.min.css' %}">   
    <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/sidebars.css' %}">
    
    <script src="{% static 'jQuery/jquery.js' %}"></script>
    <script src="{% static 'bs/js/bootstrap.bundle.min.js' %}"></script>
    <script src="{% static 'fa/js/all.min.js' %}"></script>
    <script src="{% static 'js/javascript.js' %}"></script>
    
    {% block links %}
    {% endblock %}

</head>
<body id="mainBody" class="" data-theme="solarized" theme="solarized" data-media-url="{% get_media_prefix %}" style="overflow: hidden;">
    <div class="d-flex toggled" id="wrapper">
        <!-- Sidebar Wrapper (based on active controller) -->
        <div class="camaSidebar">
            {% block sidebar %}
                {% include './bars/basesidebar.html' %}
            {% endblock %}
        </div>

        <div id="pageContentWrapper">
            <!-- Top Navbar Content -->
            {% block header %}
                {% include './bars/header.html' %}
            {% endblock %}
 
            <!-- Begin page content -->
            <main>
                <div class='col-12'>
                    <div class="overlay"></div>
                    <!-- Notifications -->
                    {% if messages %}
                        <div id="myToasts" aria-live="polite" aria-atomic="true" style="position: relative; z-index: 10000;">
                        {% for message in messages %}
                            <!-- Position it -->
                            <div style="position: absolute; top: 0; right: 0;">
                                <!-- Then put toasts within -->
                                <div class="toast ml-auto" role="alert" data-delay="10000" data-animation="true" aria-live="assertive" aria-atomic="true">
                                    <div class="toast-header">
                                        <!-- <img src="..." class="rounded mr-2" alt="..."> -->
                                        <strong class="mr-auto">
                                        {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}
                                            <span class="badge badge-danger" style="font-size: 100%;">
                                                &nbsp;CAMAHub Error Message&nbsp;
                                            </span>
                                        {% elif message.level == DEFAULT_MESSAGE_LEVELS.WARNING %}
                                            <span class="badge badge-warning" style="font-size: 100%;">
                                                &nbsp;CAMAHub Warning Message&nbsp;
                                            </span>
                                        {% elif message.level == DEFAULT_MESSAGE_LEVELS.DEBUG %}
                                            <span class="badge badge-info" style="font-size: 100%;">
                                                &nbsp;CAMAHub Debug Message&nbsp;
                                            </span>
                                        {% else %}
                                            <span class="badge badge-success" style="font-size: 100%;">
                                                &nbsp;CAMAHub Notification&nbsp;
                                            </span>
                                        {% endif %}
                                        </strong>
                                        <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
                                        <span aria-hidden="true">&times;</span>
                                        </button>
                                    </div>
                                    <div class="toast-body text-dark">
                                        {{ message }}
                                    </div>
                                </div>
                            </div>
                        {% endfor %}
                        </div>
                    {% endif %}
                    {% block mailcount %}
                    {% endblock %}
                    {% block content %}
                    {% endblock %}
                </div>
            </main>

            {% block footer %}
                {% include './bars/footer.html' %}
            {% endblock %}

            {% block scripts %}
            {% endblock %}
        </div>
    </div>

    <script>
        // Pass current user's username to JavaScript variable
        var currentUserUsername = "{{ request.user.username }}";

        document.addEventListener("DOMContentLoaded", function() {
            console.log("DOMContentLoaded event fired");
            
            // Check if the element with ID 'myToasts' exists
            var myToasts = document.getElementById('myToasts');
            if (myToasts) {
                console.log("Element with ID 'myToasts' found:", myToasts);
                // Your JavaScript code for toast notifications here
                var notificationDuration = 5000; // Duration in milliseconds (5 seconds)
                
                // Establish a WebSocket connection
                var socket = new WebSocket("ws://" + window.location.host + "/ws/postman/notifications/");
                
                // Event listener for when the connection is established
                socket.onopen = function(event) {
                    console.log("WebSocket connection established");
                    
                    // Simulate receiving a new message after connection is established (with a delay)
                    setTimeout(function() {
                        NewMessage('You have new message');
                    }, 5000); // Delayed simulation after 2 seconds
                };

                // Event listener for when the WebSocket connection is closed
                socket.onclose = function(event) {
                    console.log("WebSocket connection closed");
                };
                 // Event listener for incoming messages
                socket.onmessage = function(event) {
                    handleWebSocketMessage(event);
                };


                // Function to simulate receiving a new message
                function NewMessage(messageContent, recipientUsername) {
                    var messageData = {
                        notification_type: 'new_message',
                        message_content: messageContent,
                        recipient: recipientUsername // Include recipient information
                    };
                    
                    var eventData = {
                        data: JSON.stringify(messageData)
                    };

                    // Call the WebSocket onmessage event handler directly with the simulated message
                    handleWebSocketMessage(eventData);
                }

                // Function to handle WebSocket messages
                function handleWebSocketMessage(eventData) {
                    var data = JSON.parse(eventData.data);
                    console.log("Received WebSocket message:", data);
                    // Check if the notification is a new message
                    if (data.notification_type === 'new_message') {
                        console.log("Received new message notification");
                        // Check if the message is received by the current user
                        if (data.recipient === currentUserUsername) {
                            console.log("Message is intended for current user");
                            // Show toast notification only for received messages
                            showToast('New message received: ' + data.message_content);
                        } else {
                            console.log("Message is not intended for current user");
                        }
                    }
                }

                // Function to show toast notification
                function showToast(message) {
                    var toast = document.createElement('div');
                    toast.className = 'toast';
                    toast.setAttribute('role', 'alert');
                    toast.setAttribute('aria-live', 'assertive');
                    toast.setAttribute('aria-atomic', 'true');
                    toast.innerHTML = `
                        <div class="toast-header">
                            <strong class="mr-auto">New Message Received</strong>
                            <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="toast-body">
                            ${message}
                        </div>
                    `;

                    // Display the toast notification if the element exists
                    myToasts.appendChild(toast);
                    // Show the toast
                    $(toast).toast('show');
                    
                    // Set a timeout to hide the toast after a longer duration
                    setTimeout(function() {
                        $(toast).toast('hide');
                    }, notificationDuration);
                }
            } else {
                console.error("Element with ID 'myToasts' not found");
            }
        });
    </script>


    <script>
        $(document).ready(function() {
            $("body").tooltip({ selector: '[data-toggle=tooltip]' });
            // $(#cecIntroContainer).focus();

            {% if messages %}
                $('.toast').toast('show');
            {% else %}
                $('.toast').toast('hide');
            {% endif %}
        });
    </script>
    

</body>
</html>
import json  # noqa: D100
import logging

from channels.generic.websocket import AsyncWebsocketConsumer

logger = logging.getLogger(__name__)


class NotificationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def send_message(self, message):
        await self.send(text_data=json.dumps({'message': message}))

    async def receive(self, text_data):
        try:
            message = json.loads(text_data)
            recipient = message.get('recipient')
            current_user = self.scope['user'].username

            if message.get('notification_type') == 'new_message':
                if recipient == current_user:
                    await self.send(
                        text_data=json.dumps(
                            {'notification_type': 'new_message', 'message_content': message['message_content']}
                        )
                    )
        except json.JSONDecodeError as e:
            await self.send(text_data=json.dumps({'error': 'Invalid JSON format'}))
        except KeyError as e:
            await self.send(text_data=json.dumps({'error': 'Missing key in message'}))
        except Exception as e:
            await self.send(text_data=json.dumps({'error': 'Unexpected error'}))

    async def disconnect(self, close_code):
        pass

Side note: You’ve a case where you have the ``` on the same line as some other text - I’ve fixed that for you.

You need to go back and reread my previous reply at integrating Django-postman - #6 by KenWhitesell

Every browser is connected to its own instance of a consumer. It’s not like there are multiple connections sharing the same consumer instance.

Sending messages between users requires that the message be passed from the consumer for the originating source, through the channel layer, to the consumer for the destination, where it can then be sent out to the browser. This means that the originating consumer needs to know a channel that the destination consumer is listening to - and that’s where the “one-person group” comes in to play.