I have implemented a Django Channel for real time messages between Group users.
It is working fine on the local system but when I push this to the server, I am getting the error:
WebSocket connection to 'ws://nailsent.developmentrecords.com/ws/chat/13/' failed: Error during WebSocket handshake: Unexpected response code: 404 (anonymous) @ 13:465
This is my script
<script>
const currentDate = new Date();
// Get the month abbreviation
const month = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(currentDate);
const day = currentDate.getDate();
const year = currentDate.getFullYear();
let hours = currentDate.getHours();
const ampm = hours >= 12 ? 'p.m.' : 'a.m.';
hours = hours % 12 || 12; // Convert 24-hour to 12-hour format
// Get the minutes
const minutes = currentDate.getMinutes();
// Combine all parts into the desired format
const formattedTime = '${month}. ${day}, ${year}, ${hours}:${minutes} ${ampm}';
const card = document.querySelector('.card-body');
const sendMessageInput = document.getElementById('sendMessageInput');
const sendButton = document.getElementById('sendButton');
const group_id = "{{ group_id }}";
const userImg = "{{user_image}}"
const username = "{{request.user.username}}";
const chatSocket = new WebSocket('wss://${window.location.host}/ws/chat/${group_id}/');
chatSocket.onopen = function (event) {
console.log('WebSocket connection established.');
};
document.addEventListener("keydown", (e)=>{
if (e.keyCode === 13){
sendMessage();
}
})
// Event listener for send button click
sendButton.addEventListener('click', sendMessage);
scrollToBottom();
// Event listener for incoming messages
if ("Notification" in window) {
// Request permission from the user to display notifications
if (Notification.permission !== "granted") {
Notification.requestPermission();
}
}
// Function to send a notification
function showCustomNotification(username, message, image) {
const customNotification = document.getElementById('custom-notification');
customNotification.querySelector('.username').innerText = username;
customNotification.querySelector('.message').innerText = message;
const imageElement = customNotification.querySelector('.image');
imageElement.src = image;
customNotification.style.display = 'block';
// setTimeout(() => {
// customNotification.style.display = 'none';
// }, 5000); // Hides the notification after 5 seconds (adjust as needed)
}
// Function to append received message to receiver box
chatSocket.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
if (data.message) {
// Check if the message is from another user
if (data.username !== username) {
appendReceivedMessage(data.username, data.message, data.image);
showCustomNotification(data.username, data.message, data.image)
}
}
} catch (error) {
console.error('Error processing message:', error);
}
};
// Function to append received message to receiver box
function appendReceivedMessage(username, message, image) {
const messageHTML = '<div class="receiver-box">
<div class="row">
<div class="col-1 d-flex align-items-end">
<img src="${image ? image : "{% static 'all_pages/images/icons/message-def-img.png' %}" }" class="sender-image"
alt="User Image"/>
</div>
<div class="col-11 ps-3 mb-2">
<div class="ps-2 text-secondary d-block mb-1 text-left"><small>${username}</small></div>
<div class="msg-box received-msg receiver-box1">${message}</div>
<p class="time">${formattedTime}</p>
</div>
</div>
</div>';
// Create a temporary container element
const container = document.createElement('div');
container.classList.add("col-12");
container.innerHTML = messageHTML;
// Append the container's child to the desired parent element
card.appendChild(container);
scrollToBottom()
}
// Function to send message
function sendMessage() {
const message = sendMessageInput.value.trim();
if (message !== '') {
appendSentMessage(message);
chatSocket.send(JSON.stringify({
'message': message,
'username': username,
'group_id': group_id,
'image': userImg,
}));
sendMessageInput.value = '';
}
}
// Function to append sent message to sender box
function appendSentMessage(message) {
const messageHTML = '<div class="sender-box">
<div class="row">
<div class="col-11 d-flex align-items-end flex-column ps-3 mb-2">
<div class="ps-2 text-secondary d-block mb-1 text-right"><small>${username}</small></div>
<div class="msg-box sent-msg sender-box1">${message}</div>
<p class="time">${formattedTime}</p>
</div>
<div class="col-1 d-flex justify-content-end">
<div class="d-flex justify-content- align-items-end">
<img src=${userImg} class="receiver-image">
</div>
</div>
</div>
</div>';
// Create a temporary container element
const container = document.createElement('div');
container.classList.add("col-12");
container.innerHTML = messageHTML;
// Append the container's child to the desired parent element
card.appendChild(container);
scrollToBottom()
}
function scrollToBottom() {
card.scrollTop = card.scrollHeight;
}
</script>
Consumer.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "nailSent_porject.settings")
from channels.layers import get_channel_layer
import django
django.setup()
from channels.generic.websocket import AsyncWebsocketConsumer
import json
from asgiref.sync import sync_to_async
from .models import Message, Group, User
# Now you can access settings like this
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_id = self.scope['url_route']['kwargs']['group_id']
self.group_name = f'{self.group_id}'
# Join group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
url_pattern = self.scope['url_route']['route']
print(" this is a url=========================", url_pattern)
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
async def receive(self, text_data):
try:
data = json.loads(text_data)
message = data['message']
username = data['username']
group = data['group_id']
image = data['image']
# Send message to room group
await self.save_message(message,username,group)
await self.channel_layer.group_send(
self.group_name,
{
'type': 'chat_message',
'message': message,
'username': username,
'image':image,
}
)
except json.JSONDecodeError:
self.send(data=json.dumps({
'error': 'Invalid JSON format.'
}))
except Exception as e:
self.send(data=json.dumps({
'error': str(e)
}))
# Receive message from room group
async def chat_message(self, event):
try:
message = event['message']
username = event['username']
image = event['image']
# sending message to websocket
await self.send(text_data=json.dumps({
'message': message,
'username': username,
'image':image,
}))
except KeyError:
print("Error: 'message' key not found in event:", event)
except Exception as e:
print("Error in chat_message:", e)
@sync_to_async
def save_message(self, message,username, group_id):
user = User.objects.get(username=username)
group = Group.objects.get(id=group_id)
Message.objects.create(content=message,sender=user,group=group)
routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'wss/chat/(?P<group_id>[^/]+)/$', consumers.ChatConsumer.as_asgi()),
]
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})
I have also created a file with the name of Daphne.socket
and daphne.service
, however, daphne gets started on the server but gives me the above error.
Try adding /ws/
location to nginx
configuration file for your project. For example:
location /ws/ {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
In this example websocket connection is in 8000
port