Skip to content

Commit

Permalink
Merge pull request #235 from TheSecretOrganization/157-implement-chat
Browse files Browse the repository at this point in the history
157 implement chat
  • Loading branch information
Thibrac authored Oct 15, 2024
2 parents 3af7958 + 85e0794 commit 68b8ec2
Show file tree
Hide file tree
Showing 17 changed files with 643 additions and 6 deletions.
Empty file added django/src/chat/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions django/src/chat/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions django/src/chat/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ChatConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'chat'
113 changes: 113 additions & 0 deletions django/src/chat/consumers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import json
from channels.generic.websocket import AsyncWebsocketConsumer
import redis.asyncio as redis

class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.username = self.scope['user'].username if self.scope['user'].is_authenticated else 'Anonyme'
self.redis = redis.Redis(host="redis", decode_responses=True)

if self.scope['url_route']['kwargs'].get('username'):
other_username = self.scope['url_route']['kwargs']['username']
users = sorted([self.username, other_username])
self.room_group_name = f'private_chat_{users[0]}_{users[1]}'
else:
self.room_group_name = 'chat_general'

await self.channel_layer.group_add(f'user_{self.username}', self.channel_name)
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
await self.add_active_user()
await self.accept()
await self.broadcast_active_users()

if self.scope['url_route']['kwargs'].get('username'):
await self.notify_other_user(other_username)

async def disconnect(self, close_code):
await self.channel_layer.group_discard(f'user_{self.username}', self.channel_name)
await self.remove_active_user()
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
await self.broadcast_active_users()
await self.redis.close()

async def receive(self, text_data):
text_data_json = json.loads(text_data)
if text_data_json.get('type') == 'fetch_history':
await self.send_chat_history()
else:
message = text_data_json['message']
username = self.scope['user'].username if self.scope['user'].is_authenticated else 'Anonyme'

formatted_message = json.dumps({
'message': message,
'username': username
})

await self.redis.rpush(f'chat_{self.room_group_name}_messages', formatted_message)

await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username,
}
)

async def notify_other_user(self, other_username):
other_user_channel = f'user_{other_username}'
await self.channel_layer.group_send(
other_user_channel,
{
'type': 'private_message_notification',
'from_user': self.username,
}
)

async def send_chat_history(self):
messages = await self.redis.lrange(f'chat_{self.room_group_name}_messages', 0, -1)
history = [json.loads(message) for message in messages]

await self.send(text_data=json.dumps({
'type': 'chat_history',
'messages': history
}))

async def chat_message(self, event):
message = event['message']
username = event['username']

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

async def private_message_notification(self, event):
from_user = event['from_user']
await self.send(text_data=json.dumps({
'type': 'private_message_notification',
'from_user': from_user,
}))

async def add_active_user(self):
await self.redis.sadd('active_users', self.username)

async def remove_active_user(self):
await self.redis.srem('active_users', self.username)

async def broadcast_active_users(self):
active_users = await self.redis.smembers('active_users')
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'send_active_users',
'users': list(active_users),
}
)

async def send_active_users(self, event):
users = event['users']
await self.send(text_data=json.dumps({
'type': 'active_users',
'users': users
}))
Empty file.
3 changes: 3 additions & 0 deletions django/src/chat/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
7 changes: 7 additions & 0 deletions django/src/chat/routing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import re_path
from .consumers import ChatConsumer

websocket_urlpatterns = [
re_path(r'ws/chat/General/$', ChatConsumer.as_asgi()),
re_path(r'ws/chat/private/(?P<username>\w+)/$', ChatConsumer.as_asgi()),
]
3 changes: 3 additions & 0 deletions django/src/chat/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
6 changes: 6 additions & 0 deletions django/src/chat/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path
from . import views

urlpatterns = [

]
3 changes: 3 additions & 0 deletions django/src/chat/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render


3 changes: 2 additions & 1 deletion django/src/core/routing.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from games.routing import websocket_urlpatterns as games_websocket_urlpatterns
from chat.routing import websocket_urlpatterns as chat_websocket_urlpatterns

websocket_urlpatterns = games_websocket_urlpatterns
websocket_urlpatterns = games_websocket_urlpatterns + chat_websocket_urlpatterns
1 change: 1 addition & 0 deletions django/src/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
'ft_auth',
'games',
'pages',
'chat',
]

MIDDLEWARE = [
Expand Down
Loading

0 comments on commit 68b8ec2

Please sign in to comment.