aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chat/__init__.py0
-rw-r--r--chat/admin.py3
-rw-r--r--chat/apps.py6
-rw-r--r--chat/consumers.py40
-rw-r--r--chat/migrations/__init__.py0
-rw-r--r--chat/models.py3
-rw-r--r--chat/routing.py6
-rw-r--r--chat/tests.py3
-rw-r--r--chat/views.py3
-rw-r--r--requirements.txt2
-rw-r--r--static/js/chat.js50
-rw-r--r--templates/blog/home.html16
-rw-r--r--thatcomputerscientist/asgi.py15
-rw-r--r--thatcomputerscientist/settings.py11
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