diff options
| -rw-r--r-- | chat/__init__.py | 0 | ||||
| -rw-r--r-- | chat/admin.py | 3 | ||||
| -rw-r--r-- | chat/apps.py | 6 | ||||
| -rw-r--r-- | chat/consumers.py | 40 | ||||
| -rw-r--r-- | chat/migrations/__init__.py | 0 | ||||
| -rw-r--r-- | chat/models.py | 3 | ||||
| -rw-r--r-- | chat/routing.py | 6 | ||||
| -rw-r--r-- | chat/tests.py | 3 | ||||
| -rw-r--r-- | chat/views.py | 3 | ||||
| -rw-r--r-- | requirements.txt | 2 | ||||
| -rw-r--r-- | static/js/chat.js | 50 | ||||
| -rw-r--r-- | templates/blog/home.html | 16 | ||||
| -rw-r--r-- | thatcomputerscientist/asgi.py | 15 | ||||
| -rw-r--r-- | thatcomputerscientist/settings.py | 11 |
14 files changed, 141 insertions, 17 deletions
diff --git a/chat/__init__.py b/chat/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/chat/__init__.py diff --git a/chat/admin.py b/chat/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/chat/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/chat/apps.py b/chat/apps.py new file mode 100644 index 00000000..5f75238d --- /dev/null +++ b/chat/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ChatConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "chat" diff --git a/chat/consumers.py b/chat/consumers.py new file mode 100644 index 00000000..be1cf265 --- /dev/null +++ b/chat/consumers.py @@ -0,0 +1,40 @@ +import json +from channels.generic.websocket import WebsocketConsumer +from asgiref.sync import async_to_sync + +class ChatConsumer(WebsocketConsumer): + def connect(self): + self.room_group_name = 'chat' + async_to_sync(self.channel_layer.group_add)( + self.room_group_name, + self.channel_name + ) + self.accept() + + def receive(self, text_data): + text_data_json = json.loads(text_data) + message = text_data_json['message'] + username = text_data_json['username'] + async_to_sync(self.channel_layer.group_send)( + self.room_group_name, + { + 'type': 'chat', + 'message': message, + 'username': username + } + ) + + def chat(self, event): + message = event['message'] + username = event['username'] + self.send(text_data=json.dumps({ + 'message': message, + 'username': username + })) + + def disconnect(self, close_code): + async_to_sync(self.channel_layer.group_discard)( + self.room_group_name, + self.channel_name + ) + diff --git a/chat/migrations/__init__.py b/chat/migrations/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/chat/migrations/__init__.py diff --git a/chat/models.py b/chat/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/chat/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/chat/routing.py b/chat/routing.py new file mode 100644 index 00000000..69101be1 --- /dev/null +++ b/chat/routing.py @@ -0,0 +1,6 @@ +from django.urls import re_path +from . import consumers + +websocket_urlpatterns = [ + re_path(r'ws/chat', consumers.ChatConsumer.as_asgi()), +] diff --git a/chat/tests.py b/chat/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/chat/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/chat/views.py b/chat/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/chat/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/requirements.txt b/requirements.txt index a160015e..4145d5ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,5 @@ fuzzywuzzy python-Levenshtein selenium django-haystack +channels +daphne diff --git a/static/js/chat.js b/static/js/chat.js new file mode 100644 index 00000000..45486286 --- /dev/null +++ b/static/js/chat.js @@ -0,0 +1,50 @@ +$(document).ready(function(){ + function createMessageElement(message, username, type) { + var messageElement = document.createElement('div'); + var username = username || 'Anonymous'; + messageElement.classList.add('message'); + switch(type) { + case 'connect': + messageElement.innerHTML = '<span style="color: #a0e9ff;">' + "<b>" + username + "</b>: " + message + '</span>'; + break; + case 'disconnect': + messageElement.innerHTML = '<span style="color: #ff9494;">' + "<b>" + username + "</b>: " + message + '</span>'; + break; + case 'default': + messageElement.innerHTML ="<b>" + username + "</b>: " + message; + break; + } + $('#messages').append(messageElement); + $('#messages').animate({scrollTop: $('#messages').prop("scrollHeight")}, 100); + $('#chatbox-input').val(""); + } + + var protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; + var URL = protocol + window.location.host + '/ws/chat/'; + + var ws = new WebSocket(URL); + ws.onopen = function(e) { + createMessageElement('You are now connected to the Chat Server!', 'System', 'connect'); + } + ws.onerror = function(e) { + createMessageElement('Connection to the Chat Server failed!', 'System', 'disconnect'); + } + ws.onclose = function(e) { + createMessageElement('You have been disconnected from the Chat Server!', 'System', 'disconnect'); + } + ws.onmessage = function(e) { + var data = JSON.parse(e.data); + createMessageElement(data['message'].trim(), data['username'], 'default'); + } + + $('#chatbox-input').on('keyup', function(e) { + if (e.keyCode == 13) { + var message = $('#chatbox-input').val(); + ws.send(JSON.stringify({ + 'message': message, + 'username': username + })); + $('#chatbox-input').val(""); + } + }); +});
\ No newline at end of file diff --git a/templates/blog/home.html b/templates/blog/home.html index 4bb5c003..3fa8c524 100644 --- a/templates/blog/home.html +++ b/templates/blog/home.html @@ -59,21 +59,7 @@ {% include 'blog/partials/mathjax.html' %} <script type="text/javascript"> var username = "{{ user.username }}" ? "{{ user.username }}" : "Anonymous"; - $(document).ready(function() { - $('#chatbox-input').on('keyup', function(e) { - if (e.keyCode === 13) { - var msg = $('#chatbox-input').val(); - if (msg.trim().length > 0) { - var message = document.createElement("div"); - message.className = "message"; - message.innerHTML = "<b>" + username + "</b>: " + msg; - $('#messages').append(message); - $('#messages').animate({scrollTop: $('#messages').prop("scrollHeight")}, 100); - } - $('#chatbox-input').val(""); - } - }); - }); </script> +<script src="{% static 'js/chat.js' %}"></script> {% endblock %} diff --git a/thatcomputerscientist/asgi.py b/thatcomputerscientist/asgi.py index dd6bcf94..11e01fe6 100644 --- a/thatcomputerscientist/asgi.py +++ b/thatcomputerscientist/asgi.py @@ -10,7 +10,20 @@ https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ import os from django.core.asgi import get_asgi_application +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.auth import AuthMiddlewareStack +import chat.routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'thatcomputerscientist.settings') -application = get_asgi_application() +# application = get_asgi_application() + +application = ProtocolTypeRouter({ + "http": get_asgi_application(), + "websocket": AuthMiddlewareStack( + URLRouter( + chat.routing.websocket_urlpatterns + ) + ), +}) + diff --git a/thatcomputerscientist/settings.py b/thatcomputerscientist/settings.py index 514adb32..dbc4f102 100644 --- a/thatcomputerscientist/settings.py +++ b/thatcomputerscientist/settings.py @@ -38,6 +38,8 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # Application definition INSTALLED_APPS = [ + 'daphne', + 'channels', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -55,6 +57,7 @@ INSTALLED_APPS = [ 'dev_status', 'announcements', 'ignis', + 'chat', ] SITE_ID = 1 @@ -105,8 +108,14 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'thatcomputerscientist.wsgi.application' +# WSGI_APPLICATION = 'thatcomputerscientist.wsgi.application' +ASGI_APPLICATION = 'thatcomputerscientist.asgi.application' +CHANNEL_LAYERS = { + "default": { + "BACKEND": "channels.layers.InMemoryChannelLayer" + } +} # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases |
