I have hosted a django application on Linode which includes a chatting functionality. I am using django-channels to implement that functionality. I am not using any particular
technology like Redis, just the inbuilt django thing to implement the websockets because I am just testing the site for now. The following is the chatroom.html file:
{% extends 'common_anime_chat/base.html' %}
{% load static %}
{% block stylesheet %}
<link rel="stylesheet" type="text/css" href="{% static 'chatroom.css' %}">
{% endblock stylesheet %}
{% block content %}
<div class="chat-container">
<div id="chat-messages" style='height: 75vh;'></div>
<div id="input-container">
<textarea id="input" rows="1"></textarea>
<input type="button" id="submit" value="Send">
</div>
</div>
{{ request.user.username|json_script:"user_username" }}
{{ request.user.profile.profile_picture.url|json_script:"user_pfp"}}
<script>
const hardcodedProfilePictures = {
'itsmyname': '/media/profile_pictures/profile1.png',
'user123': '/media/profile_picturues/profile2.png',
// Add more hardcoded profile pictures here for other usernames
};
document.querySelector('#submit').onclick = function (e) {
const messageInputDOM = document.querySelector('#input');
const message = messageInputDOM.value;
chatSocket.send(JSON.stringify({
'message': message,
'username': userUsername,
'user_pfp':userPfp,
'room_name':roomName
}));
messageInputDOM.value = '';
};
const chatMessagesDOM = document.querySelector('#chat-messages');
const userUsername = JSON.parse(document.getElementById('user_username').textContent);
// console.log(userUsername)
const pathArray = window.location.pathname.split('/');
const roomName = pathArray[pathArray.length - 2]; // Use -1 if the room name is in the last segment
let userPfp = JSON.parse(document.getElementById('user_pfp').textContent);
// Object to store profile pictures associated with usernames
let profilePictures = {};
const chatSocket = new WebSocket(window.location.protocol === 'https:' ? 'wss://' : 'ws://') +
window.location.host +
'/chat/' +
roomName +
'/'
);
chatSocket.onopen = function (event) {
console.log('WebSocket connection opened.');
};
console.log('WebSocket URL:', chatSocket.url);
chatSocket.onclose = function (event) {
console.log('WebSocket connection closed.');
};
chatSocket.onerror = function (error) {
console.log('WebSocket error:', error);
};
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
const messageContainer = document.createElement('div');
messageContainer.classList.add('chat-message');
let profilePicture = document.createElement('img');
profilePicture.src = data.user_pfp;
profilePicture.alt = 'Profile Picture';
profilePicture.style.maxHeight = '42px';
profilePicture.style.borderRadius = '50%';
profilePicture.classList.add('profile-picture');
messageContainer.appendChild(profilePicture);
const messageContent = document.createElement('span');
messageContent.classList.add('message-content');
const usernameSpan = document.createElement('span');
usernameSpan.classList.add('username');
usernameSpan.textContent = data.username + ': ';
usernameSpan.style.color = 'white';
messageContent.appendChild(usernameSpan);
const messageSpan = document.createElement('span');
messageSpan.textContent = data.message;
messageSpan.style.color = 'white';
messageContent.appendChild(messageSpan);
messageContainer.appendChild(messageContent);
chatMessagesDOM.appendChild(messageContainer);
};
// Fetch and load profile pictures for each user
fetch('/api/get_user_profile_pictures/') // Change this URL to the endpoint that provides profile picture URLs for all users
.then(response => response.json())
.then(data => {
profilePictures = data;
console.log(profilePictures)
})
.catch(error => {
console.log(profilePictures)
console.error('Error loading profile pictures:', error);
});
document.querySelector('#input').addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
// Prevent the default behavior of Enter key (form submission)
event.preventDefault();
// Trigger the click event of the submit button
document.querySelector('#submit').click();
}
});
</script>
{% endblock content %}
The following is the routing.py file:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'^ws/chat/(?P<room_name>w+)/$', consumers.ChatRoomConsumer.as_asgi())
]
The following is the output in the console:
WebSocket connection to 'ws://my.ip.address/chat/AUdZzo8slKKwKvzmUrnmLRfnVkuAYfJj/' failed:
(anonymous) @ (index):92
(index):109 WebSocket error: Event {isTrusted: true, type: 'error', target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}
The site was working perfectly fine along with the Websockets on localhost.
Although it’s highly unlikely that there’s an error in my consumers.py file cuz I don’t think the code flow is even reaching there, the following is the consumers.py file:
import AsyncWebsocketConsumer
import json
from django.contrib.auth.models import User
# from channels.db import database_sync_to_async
from asgiref.sync import sync_to_async
from .models import ChatRoom, Message, Profile
from asyncer import asyncify
@sync_to_async
def save_message_to_database(chatroom_name, username, message):
try:
chatroom = ChatRoom.objects.get(name=chatroom_name)
user = User.objects.get(username=username)
user_profile = Profile.objects.get(user=user)
new_message = Message.objects.create(chat_room=chatroom, sender=user_profile, content=message)
print("Message saved to DB:", new_message)
except ChatRoom.DoesNotExist:
print(f"Chatroom with name '{chatroom_name}' does not exist.")
except Profile.DoesNotExist:
print(f"User profile with username '{username}' does not exist.")
except Exception as e:
print(f"Error occurred while saving the message: {e}")
@sync_to_async
def get_chatroom_by_name(name):
try:
return ChatRoom.objects.get(name=name)
except ChatRoom.DoesNotExist:
return None
@sync_to_async
def get_messages_for_chatroom(chatroom):
return list(Message.objects.filter(chat_room=chatroom).order_by('timestamp'))
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
print("Before getting chatroom")
chatroom = await get_chatroom_by_name(self.room_name)
print("chatroom:",chatroom)
print("Chatroom:",chatroom)
messages = await get_messages_for_chatroom(chatroom.id)
for message in messages:
print("SENDING MESSAGE TO FUNCTION")
await self.send_message_to_client(message)
async def send_message_to_client(self, message):
user_id = await sync_to_async(lambda: message.sender.user_id)()
user = await sync_to_async(lambda: User.objects.get(id=user_id))()
print("SENDING MESSAGES TO THE FRONTEND")
await self.send(text_data=json.dumps({
'message': message.content,
'username': user.username,
'user_pfp': message.sender.profile_picture.url,
'room_name': self.room_name,
}))
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['username']
user_pfp = text_data_json['user_pfp']
room_name = text_data_json['room_name']
await save_message_to_database(chatroom_name=room_name, username=username, message=message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'username': username,
'user_pfp': user_pfp,
'room_name': room_name,
'tester_message': 'tester message in receive function'
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
user_pfp = event['user_pfp']
await self.send(text_data=json.dumps({
'message': message,
'username': username,
'user_pfp': user_pfp,
'tester_message': 'tester message in chatroom message'
}))
Please help me out with this problem!