Compare commits

..

No commits in common. "42287d4a869a7720771865cdf8736793673396fd" and "d03f90367f1fd075e79f4a69d31c5a49b9e53d01" have entirely different histories.

16 changed files with 454 additions and 55 deletions

View File

@ -22,17 +22,17 @@ pip install -r requirements.txt
``` ```
- Setup database - Setup database
``` ```
<<<<<<< HEAD
python manage.py runserver makemigrations profiles
python manage.py migrate profiles
=======
python manage.py makemigrations games python manage.py makemigrations games
python manage.py makemigrations profiles python manage.py makemigrations profiles
python manage.py runserver makemigrations chat python manage.py runserver makemigrations chat
python manage.py migrate python manage.py migrate
>>>>>>> d03f90367f1fd075e79f4a69d31c5a49b9e53d01
``` ```
- Start the developpement server - Start the developpement server
``` ```
python manage.py runserver 0.0.0.0:8000 python manage.py runserver 0.0.0.0:8000
``` ```
coc nvim
```
pip install django-stubs
```

View File

@ -1,3 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import ChannelModel, MemberModel, MessageModel
# Register your models here. admin.site.register(ChannelModel)
admin.site.register(MemberModel)
admin.site.register(MessageModel)

View File

@ -1,10 +1,24 @@
import json import json
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from .models import MemberModel, MessageModel
import time
class ChatConsumer(WebsocketConsumer): class ChatConsumer(WebsocketConsumer):
def connect(self): def connect(self):
self.room_group_name = 'test' channel_id : str = self.scope['path'].split('/')[3]
self.room_group_name = 'chat' + channel_id
user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
if MemberModel.objects.filter(member_id=user.pk, channel_id=int(channel_id)).count() != 1:
return
if (self.channel_layer == None):
return
async_to_sync(self.channel_layer.group_add)( async_to_sync(self.channel_layer.group_add)(
self.room_group_name, self.room_group_name,
@ -14,31 +28,58 @@ class ChatConsumer(WebsocketConsumer):
self.accept() self.accept()
def receive(self, text_data): def receive(self, text_data=None, bytes_data=None):
if text_data == None:
return
text_data_json = json.loads(text_data) text_data_json = json.loads(text_data)
message = text_data_json['message'] message = text_data_json['message']
channel_id : int = int(self.scope['path'].split('/')[3])
user = self.scope["user"] user = self.scope["user"]
if user.is_anonymous: if (user.is_anonymous or not user.is_authenticated):
return; return
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
return
if (self.channel_layer == None):
return
print(message)
message_time : int = int(time.time() * 1000)
async_to_sync(self.channel_layer.group_send)( async_to_sync(self.channel_layer.group_send)(
self.room_group_name, self.room_group_name,
{ {
'type':'chat_message', 'type':'chat_message',
'username':user.username, 'author_id':user.pk,
'message':message 'content':message,
'time':message_time,
} }
) )
new_message = MessageModel()
new_message.channel_id = channel_id
new_message.author_id = user.pk
new_message.content = message
new_message.time = message_time
new_message.save()
def chat_message(self, event): def chat_message(self, event):
channel_id : int = int(self.scope['path'].split('/')[3])
user = self.scope["user"] user = self.scope["user"]
if user.is_anonymous: if (user.is_anonymous or not user.is_authenticated):
return; return
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
return
self.send(text_data=json.dumps({ self.send(text_data=json.dumps({
'type':'chat', 'type':'chat',
'username':event['username'], 'author_id':event['author_id'],
'message':event['message'] 'content':event['content'],
'time': event['time'],
})) }))

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.db.models import IntegerField from django.db.models import IntegerField
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib import admin
# Create your models here. # Create your models here.
class ChannelModel(models.Model): class ChannelModel(models.Model):
@ -10,8 +11,14 @@ class MemberModel(models.Model):
member_id = IntegerField(primary_key=False) member_id = IntegerField(primary_key=False)
channel_id = IntegerField(primary_key=False) channel_id = IntegerField(primary_key=False)
def __str__(self):
return "member_id: " + str(self.member_id) + ", channel_id: " + str(self.channel_id)
class MessageModel(models.Model): class MessageModel(models.Model):
channel_id = IntegerField(primary_key=False) channel_id = IntegerField(primary_key=False)
author_id = IntegerField(primary_key=False) author_id = IntegerField(primary_key=False)
content = models.CharField(max_length=255) content = models.CharField(max_length=255)
time = models.DateField() time = IntegerField(primary_key=False)
def __str__(self):
return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content

View File

@ -2,5 +2,5 @@ from django.urls import re_path
from . import consumers from . import consumers
websocket_urlpatterns = [ websocket_urlpatterns = [
re_path(r'ws/socket-server/', consumers.ChatConsumer.as_asgi()) re_path(r'ws/chat/(?P<chat_id>\d+)/$', consumers.ChatConsumer.as_asgi())
] ]

View File

@ -6,4 +6,5 @@ from . import views
urlpatterns = [ urlpatterns = [
path("<int:pk>", views.ChatView.as_view(), name="chat_page"), path("<int:pk>", views.ChatView.as_view(), name="chat_page"),
path("", views.ChatsView.as_view(), name="chats_page"),
] ]

View File

@ -1,29 +1,100 @@
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import authentication, permissions from rest_framework import authentication, permissions, status
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from .models import ChannelModel, MemberModel, MessageModel from .models import ChannelModel, MemberModel, MessageModel
from django.core import serializers
class ChatView(APIView): class ChatView(APIView):
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,) authentication_classes = (SessionAuthentication,)
def post(self, request, pk): def get(self, request, pk):
if (ChannelModel.objects.filter(pk=pk)): if (ChannelModel.objects.filter(pk=pk)):
return Response("Channel already exist") return Response({'channel_id': pk})
else:
return Response("Channel doesn't exist")
def delete(self, request, pk):
ChannelModel.objects.filter(pk=pk).delete()
MessageModel.objects.filter(pk=pk).delete()
MemberModel.objects.filter(pk=pk).delete()
return Response({'channel_id': pk})
"""
def post(self, request, pk):
channel = ChannelModel.objects.filter(pk=pk)
message = request.data.get("message", [])
print(message)
if (message == []):
return Response('No message', status=status.HTTP_400_BAD_REQUEST)
new_message = MessageModel()
new_message.channel_id = message["channel_id"]
new_message.author_id = message["author_id"]
new_message.content = message["content"]
new_message.time = message["time"]
new_message.save()
messages = MessageModel.objects.filter(channel_id=pk)
messages = serializers.serialize("json", messages)
return Response({'messages':messages}, status=status.HTTP_200_OK)
"""
class ChatsView(APIView):
def post(self, request):
data: dict = request.data data: dict = request.data
users_id = request.data.get("users_id", []) users_id = request.data.get("users_id", [])
if len(users_id) < 2: if len(users_id) < 2:
return Response("Not enought members to create the channel") return Response('Not enought members to create the channel', status=status.HTTP_400_BAD_REQUEST)
if users_id[0] == users_id[1]:
return Response('Same member', status=status.HTTP_400_BAD_REQUEST)
for user_id1 in users_id:
for member1 in MemberModel.objects.filter(member_id=user_id1):
for user_id2 in users_id:
if user_id1 == user_id2:
continue
for member2 in MemberModel.objects.filter(member_id=user_id2):
if (member1.channel_id == member2.channel_id):
messages = MessageModel.objects.filter(channel_id=member1.channel_id)
messages = serializers.serialize("json", messages)
return Response({'channel_id': member1.channel_id, 'messages':messages}, status=status.HTTP_200_OK)
new_channel = ChannelModel() new_channel = ChannelModel()
new_channel.save() new_channel.save()
for user_id in users_id: for user_id in users_id:
new_member = MemberModel() new_member = MemberModel()
new_member.channel_id = new_channel.pk new_member.channel_id = new_channel.pk
new_member.member_id = user_id new_member.member_id = user_id
new_member.save() new_member.save()
return Response("Channel created") return Response({'channel_id': new_channel.pk}, status=status.HTTP_201_CREATED)
def delete(self, request):
data: dict = request.data
users_id = request.data.get("users_id", [])
#print(list(MemberModel.objects.all()))
for user_id1 in users_id:
for member1 in MemberModel.objects.filter(member_id=user_id1):
for user_id2 in users_id:
if user_id1 == user_id2:
break
for member2 in MemberModel.objects.filter(member_id=user_id2):
if (member1.channel_id == member2.channel_id):
MessageModel.objects.filter(channel_id=member1.channel_id).delete()
member1.delete()
member2.delete()
ChannelModel.objects.get(pk=member1.channel_id).delete()
return Response("Channel removed", status=status.HTTP_200_OK)
return Response("Channel doesn't exist", status=status.HTTP_404_NOT_FOUND)

View File

@ -15,3 +15,56 @@
{ {
margin: 10px 10px 0 0; margin: 10px 10px 0 0;
} }
#app #chats {
overflow: hidden;
display: flex;
text-align: left;
}
#app #users {
margin-right: 50px;
}
#app #chat {
position: relative;
height: 100%;
width: 60%;
/*border: 4px solid green;*/
overflow:hidden;
/*overflow-y: scroll;
overflow-x: hidden;*/
}
#app #input_chat{
position: sticky;
bottom: 0;
/*width: calc(100% - 8px);*/
width: 100%;
border: none;
outline: none;
border-bottom: 2px solid green;
caret-color: green;
}
#app #you {
text-align: left;
position: relative;
max-width: 48%;
left: 10px;
margin: 5px 0 0 0;
color: green;
word-wrap: break-word;
}
#app #other {
text-align: right;
position: relative;
max-width: 48%;
margin: 5px 0 0 auto;
right: 10px;
color: red;
/* permet le retour à la ligne à la place de dépasser*/
word-wrap: break-word;
}

View File

@ -1,9 +1,77 @@
import {Message} from "./message.js"
class Channel { class Channel {
constructor(id, members, messages) { constructor(client, channel_id, members_id, messages, reload) {
this.id = id; this.client = client;
this.members = members; this.channel_id = channel_id;
this.messages = messages; this.members_id = members_id;
this.messages = [];
if (messages != undefined)
this.updateMessages(messages);
this.connect(reload);
} }
// reload = function to use when we receive a message
async connect(reload) {
let url = `ws://${window.location.host}/ws/chat/${this.channel_id}/`;
this.chatSocket = new WebSocket(url);
this.chatSocket.onmessage = (event) =>{
let data = JSON.parse(event.data)
this.messages.push(new Message(
this.channel_id,
data.author_id,
data.content,
data.time,
));
reload();
};
}
async disconnect() {
this.chatSocket.close();
}
updateMessages(messages) {
messages = JSON.parse(messages);
let new_messages = [];
messages.forEach((message) => {
message = message["fields"];
new_messages.push(new Message(
message["channel_id"],
message["author_id"],
message["content"],
message["time"],
));
});
//console.log(new_messages);
this.messages = new_messages;
return new_messages;
}
async sendMessageChannel(message) {
if (this.chatSocket == undefined)
return;
this.chatSocket.send(JSON.stringify({
'message':message
}));
}
async deleteChannel() {
let response = await this.client._delete("/api/chat/" + this.channel_id, {
});
let data = await response.json();
return data;
}
} }
export {Channel} export {Channel}

View File

@ -1,19 +1,37 @@
import {Channel} from "./channel.js"
import {Message} from "./message.js"
class Channels { class Channels {
constructor(client) { constructor(client) {
this.client = client; this.client = client;
} }
async createChannel(users_id) { async createChannel(users_id, reload) {
console.log(users_id);
let response = await this.client._post("/api/chat/", { let response = await this.client._post("/api/chat/", {
users_id:users_id users_id:users_id
}); });
let data = await response.json(); let data = await response.json();
if (data == "Channel already exist") let exit_code = await response.status;
return null; if (exit_code >= 300)
return undefined;
let messages = undefined;
if (exit_code == 200)
messages = data.messages;
return new Channel(this.client, data.channel_id, users_id, messages, reload);
}
async deleteChannel(users_id) {
let response = await this.client._delete("/api/chat/", {
users_id:users_id
});
let data = await response.json();
console.log(response.status)
return data; return data;
} }
} }
export {Channels} export {Channels}

View File

@ -0,0 +1,10 @@
class Message {
constructor(channel_id, author_id, content, time) {
this.channel_id = channel_id;
this.author_id = author_id;
this.content = content;
this.time = time;
}
}
export {Message}

View File

@ -2,6 +2,7 @@ import { Account } from "./account.js";
import { MatchMaking } from "./matchmaking.js"; import { MatchMaking } from "./matchmaking.js";
import { Profile } from "./profile.js"; import { Profile } from "./profile.js";
import { Profiles } from "./profiles.js"; import { Profiles } from "./profiles.js";
import { Channels } from './chat/channels.js';
function getCookie(name) function getCookie(name)
{ {
@ -22,6 +23,9 @@ class Client
this.profiles = new Profiles(this); this.profiles = new Profiles(this);
this.matchmaking = new MatchMaking(this); this.matchmaking = new MatchMaking(this);
this._logged = undefined; this._logged = undefined;
this.channels = new Channels(this);
this.channel = undefined;
} }
async isAuthentificate() async isAuthentificate()

View File

@ -4,7 +4,7 @@ export default class extends AbstractView {
constructor(params) { constructor(params) {
super(params, "Chat"); super(params, "Chat");
let url = `ws://${window.location.host}/ws/socket-server/` let url = `ws://${window.location.host}/ws/chat/`
this.chatSocket = new WebSocket(url) this.chatSocket = new WebSocket(url)
this.chatSocket.onmessage = function(e){ this.chatSocket.onmessage = function(e){

View File

@ -1,5 +1,6 @@
import AbstractView from "./AbstractView.js"; import AbstractView from "./AbstractView.js";
import {client} from "../index.js"; import {client} from "../index.js";
import {Message} from "../api/chat/message.js"
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {
@ -8,16 +9,20 @@ export default class extends AbstractView {
async postInit() { async postInit() {
let search = document.getElementById("form"); let search = document.getElementById("input_user");
search.addEventListener("input", this.users) search.addEventListener("input", this.users)
let chat_input = document.getElementById("input_chat");
//chat_input.addEventListener("keydown", this.chat_manager)
this.users(); this.users();
this.chat();
} }
async users() { async users() {
let search = document.getElementById("form").value; let search = document.getElementById("input_user").value.toLowerCase();
let logged = await client.isAuthentificate(); let logged = await client.isAuthentificate();
@ -25,7 +30,7 @@ export default class extends AbstractView {
let list_users = document.getElementById('list_users'); let list_users = document.getElementById('list_users');
list_users.innerHTML = ""; list_users.innerHTML = "";
users.filter(user => user.username.startsWith(search) == true).forEach((user) => { users.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => {
var new_user = document.createElement("li"); var new_user = document.createElement("li");
// username // username
@ -38,20 +43,41 @@ export default class extends AbstractView {
new_user.appendChild(document.createTextNode(" ")); new_user.appendChild(document.createTextNode(" "));
// chat // chat
if (logged) { if (logged && client.me.user_id != user.user_id) {
let chat = document.createElement("a"); let add_chat = document.createElement("a");
let array = [ add_chat.addEventListener("click", async () => {
client.me.user_id, if (client.channel != undefined) {
user.user_id, client.channel.members_id.forEach((member_id) => {
]; if (member_id == user.user_id)
console.log(client.me.id); client.channel = undefined;
chat.addEventListener("click", async function(){
console.log("click");
await client.channels.createChannel([client.me.user_id , user.user_id]);
}); });
//chat.href = `/chat`
chat.appendChild(document.createTextNode("Chat")); if (client.channel == undefined)
new_user.appendChild(chat); return this.hideChat();
client.channel.disconnect();
}
client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat);
this.chat();
});
add_chat.appendChild(document.createTextNode("Chat"));
new_user.appendChild(add_chat);
/*new_user.appendChild(document.createTextNode(" "));
let remove = document.createElement("a");
remove.addEventListener("click", async () => {
if (client.me.user_id != user.user_id) {
await client.channels.deleteChannel([client.me.user_id , user.user_id]);
if
client.channel.disconnect();
client.channel = undefined;
this.chat()
}
});
remove.appendChild(document.createTextNode("Remove"));
new_user.appendChild(remove);*/
} }
// break line // break line
@ -69,16 +95,113 @@ export default class extends AbstractView {
} }
async chat() {
let logged = await client.isAuthentificate();
let reload = document.getElementById("messages");
if (reload != null)
reload.remove();
reload = document.getElementById("members");
if (reload != null)
reload.remove();
if (client.channel == undefined || !logged)
return ;
let chats = document.getElementById("chats");
if (document.getElementById("chat") == null) {
let chat = document.createElement("div");
chat.id = "chat";
chats.appendChild(chat);
}
// div des messages
let messages = document.createElement("div");
messages.id = "messages";
if (document.getElementById("input_chat") == null)
chat.appendChild(messages);
else
document.getElementById("input_chat").before(messages);
// Input pour rentrer un message
if (document.getElementById("input_chat") == null) {
let chat_input = document.createElement("input");
chat_input.id="input_chat";
chat_input.type="text";
chat_input.name="message";
chat_input.placeholder="message bozo";
chat_input.maxLength=255;
chat.appendChild(chat_input);
chat_input.addEventListener("keydown", async () => {
if (event.keyCode == 13 && client.channel != undefined) {
//let chat_input = document.getElementById("input_chat");
let chat_text = chat_input.value;
await client.channel.sendMessageChannel(chat_text)
// Reset
chat_input.value = "";
}
});
}
// nom des membres du chat
let users = await client.profiles.all();
let members = document.createElement("h2");
members.id = "members";
let usernames = "";
client.channel.members_id.forEach((member_id) => {
if (member_id != client.me.user_id) {
if (usernames.length > 0)
usernames += ", ";
usernames += (users.filter(user => user.user_id == member_id)[0].username);
}
});
members.appendChild(document.createTextNode(usernames));
messages.before(members);
// les messages
client.channel.messages.forEach((message) => {
let text = document.createElement("p");
text.appendChild(document.createTextNode(message.content));
if (message.author_id == client.me.user_id)
text.id = "you";
else
text.id = "other";
messages.appendChild(text);
});
}
async hideChat() {
let close = document.getElementById("chat");
if (close != null)
close.remove();
}
async leavePage() {
if (client.channel != undefined)
client.channel.disconnect();
client.channel = undefined
}
async getHtml() { async getHtml() {
return ` return `
<link rel="stylesheet" href="/static/css/search.css"> <link rel="stylesheet" href="/static/css/search.css">
<input id="form" type="text" name="message" placeholder="userbozo"/> <div id="chats">
<div id="users"> <div id="users">
<input id="input_user" type="text" name="message" placeholder="userbozo"/>
<ul id="list_users"> <ul id="list_users">
</ul> </ul>
</div> </div>
</div>
`; `;
} }