2 Commits

Author SHA1 Message Date
42287d4a86 J'ai fais n'imp 2023-12-16 12:06:05 +01:00
a7826040d6 back to the past bcb072f7d9f5f7817d949837219231232160ff11; Chatte 2023-12-16 11:58:50 +01:00
92 changed files with 464 additions and 2286 deletions

View File

@ -22,18 +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 makemigrations chat python manage.py runserver makemigrations chat
python manage.py makemigrations tournament
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

@ -15,4 +15,4 @@ class LoggedView(APIView):
def get(self, request: HttpRequest): def get(self, request: HttpRequest):
if (request.user.is_authenticated): if (request.user.is_authenticated):
return Response({'id': request.user.pk}, status=status.HTTP_200_OK) return Response({'id': request.user.pk}, status=status.HTTP_200_OK)
return Response('false', status=status.HTTP_200_OK) return Response('false', status=status.HTTP_200_OK)

View File

@ -15,4 +15,4 @@ class RegisterView(APIView):
if user: if user:
login(request, user) login(request, user)
return Response("user created", status=status.HTTP_201_CREATED) return Response("user created", status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)

View File

@ -1,6 +1,3 @@
from django.contrib import admin from django.contrib import admin
from .models import ChatChannelModel, ChatMemberModel, ChatMessageModel
admin.site.register(ChatChannelModel) # Register your models here.
admin.site.register(ChatMemberModel)
admin.site.register(ChatMessageModel)

View File

@ -1,29 +1,10 @@
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 ChatMemberModel, ChatMessageModel
from profiles.models import BlockModel
import time
import json
class ChatConsumer(WebsocketConsumer): class ChatConsumer(WebsocketConsumer):
def connect(self): def connect(self):
self.room_group_name = 'test'
user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
channel_id : int = int(self.scope['url_route']['kwargs']['chat_id'])
self.room_group_name = f'chat{channel_id}'
if ChatMemberModel.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,
@ -33,67 +14,31 @@ class ChatConsumer(WebsocketConsumer):
self.accept() self.accept()
def receive(self, text_data=None, bytes_data=None): def receive(self, text_data):
text_data_json = json.loads(text_data)
if text_data == None: message = text_data_json['message']
return
user = self.scope["user"] user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated): if user.is_anonymous:
return return;
text_data_json: dict = json.loads(text_data)
message = text_data_json.get('message')
if (message is None):
return
receivers_id = text_data_json.get('receivers_id')
if (receivers_id is None):
return
channel_id : int = int(self.scope['url_route']['kwargs']['chat_id'])
if ChatMemberModel.objects.filter(member_id = user.pk, channel_id = channel_id).count() != 1:
return
if (self.channel_layer == None):
return
message_time: int = int(time.time() * 1000)
if (len(receivers_id) == 1 and
BlockModel.objects.filter(blocker=user.pk, blocked=receivers_id[0]) or
BlockModel.objects.filter(blocker=receivers_id[0], blocked=user.pk)
):
return
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',
'author_id':user.pk, 'username':user.username,
'content':message, 'message':message
'time':message_time,
} }
) )
new_message = ChatMessageModel(channel_id = channel_id, author_id = user.pk, content = message, time = message_time).save()
def chat_message(self, event): def chat_message(self, event):
user = self.scope["user"] user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated): if user.is_anonymous:
return return;
channel_id : int = int(self.scope['url_route']['kwargs']['chat_id'])
if ChatMemberModel.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',
'author_id':event['author_id'], 'username':event['username'],
'content':event['content'], 'message':event['message']
'time': event['time'],
})) }))

View File

@ -1,31 +1,17 @@
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 ChatChannelModel(models.Model): class ChannelModel(models.Model):
pass
def create(self, users_id: [int]):
self.save()
for user_id in users_id:
ChatMemberModel(channel_id = self.pk, member_id = user_id).save()
def get_members_id(self): class MemberModel(models.Model):
return [member_channel.member_id for member_channel in ChatMemberModel.objects.filter(channel_id = self.pk)]
class ChatMemberModel(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): class MessageModel(models.Model):
return "member_id: " + str(self.member_id) + ", channel_id: " + str(self.channel_id)
class ChatMessageModel(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 = IntegerField(primary_key=False) time = models.DateField()
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/chat/(?P<chat_id>\d+)$', consumers.ChatConsumer.as_asgi()) re_path(r'ws/socket-server/', consumers.ChatConsumer.as_asgi())
] ]

View File

@ -1,30 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from .models import ChannelModel
from profiles.models import ProfileModel class ChannelSerializer(serializers.ModelSerializer):
from .models import ChatChannelModel, ChatMessageModel
class ChatChannelSerializer(serializers.ModelSerializer):
members_id = serializers.ListField(child = serializers.IntegerField())
class Meta: class Meta:
model = ChatChannelModel model = ChannelModel
fields = ["members_id", "pk"] fields = []
def validate_members_id(self, value):
members_id: [int] = value
if len(members_id) < 2:
raise serializers.ValidationError('Not enought members to create the channel')
if len(set(members_id)) != len(members_id):
raise serializers.ValidationError('Same member')
for member_id in members_id:
if not ProfileModel.objects.filter(pk = member_id).exists():
raise serializers.ValidationError(f"The profile {member_id} doesn't exists.")
return members_id
class ChatMessageSerializer(serializers.Serializer):
class Meta:
model = ChatMessageModel
fields = ["channel_id", "author_id", "content", "time"]

View File

@ -1,30 +1,3 @@
from django.test import TestCase from django.test import TestCase
from django.test.client import Client
from django.http import HttpResponse, HttpRequest
from django.contrib.auth.models import User
# Create your tests here. # Create your tests here.
class ChatTest(TestCase):
def setUp(self):
self.client = Client()
self.username='bozo1'
self.password='password'
self.user: User = User.objects.create_user(username=self.username, password=self.password)
self.dest: User = User.objects.create_user(username="bozo2", password=self.password)
self.url = "/api/chat/"
def test_create_chat(self):
self.client.login(username=self.username, password=self.password)
response: HttpResponse = self.client.post(self.url + str(self.user.pk), {"members_id": [self.user.pk, self.dest.pk]})
response_dict: dict = eval(response.content)
self.assertDictEqual(response_dict, {})
def test_create_chat_unlogged(self):
response: HttpResponse = self.client.post(self.url + str(self.user.pk), {"members_id": [self.user.pk, self.dest.pk]})
response_dict: dict = eval(response.content)
self.assertDictEqual(response_dict, {'detail': 'Authentication credentials were not provided.'})

View File

@ -5,5 +5,5 @@ from django.conf.urls.static import static
from . import views from . import views
urlpatterns = [ urlpatterns = [
path("", views.ChannelView.as_view(), name="chats_page"), path("<int:pk>", views.ChatView.as_view(), name="chat_page"),
] ]

View File

@ -1,45 +1,29 @@
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 permissions, status from rest_framework import authentication, permissions
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from .models import ChannelModel, MemberModel, MessageModel
from django.http import HttpRequest class ChatView(APIView):
from django.contrib.auth import login
from django.db.models import QuerySet
from django.core import serializers
from .models import ChatChannelModel, ChatMemberModel, ChatMessageModel
from .serializers import ChatChannelSerializer, ChatMessageSerializer
class ChannelView(APIView):
queryset = ChatChannelModel.objects
serializer_class = ChatChannelSerializer
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,) authentication_classes = (SessionAuthentication,)
def post(self, request, pk):
if (ChannelModel.objects.filter(pk = pk)):
return Response("Channel already exist")
def post(self, request): data: dict = request.data
users_id = request.data.get("users_id", [])
if len(users_id) < 2:
return Response("Not enought members to create the channel")
serializer = self.serializer_class(data = request.data) new_channel = ChannelModel()
new_channel.save()
serializer.is_valid(raise_exception=True)
data: dict = serializer.validated_data for user_id in users_id:
new_member = MemberModel()
new_member.channel_id = new_channel.pk
new_member.member_id = user_id
new_member.save()
members_id = data.get("members_id") return Response("Channel created")
if self.request.user.pk not in members_id:
return Response({"detail": "You are trying to create a chat group without you."}, status = status.HTTP_400_BAD_REQUEST)
for member_channel in ChatMemberModel.objects.filter(member_id = members_id[0]):
channel_id: int = member_channel.channel_id
if not ChatChannelModel.objects.filter(pk = channel_id).exists():
continue
channel: ChatChannelModel = ChatChannelModel.objects.get(pk = channel_id)
if set(channel.get_members_id()) == set(members_id):
messages = ChatMessageModel.objects.filter(channel_id = channel_id).order_by("time")
messages = serializers.serialize("json", messages)
return Response({'channel_id': channel_id, 'messages': messages}, status=status.HTTP_200_OK)
new_channel_id = ChatChannelModel().create(members_id)
return Response({'channel_id': new_channel_id}, status=status.HTTP_201_CREATED)

View File

@ -9,4 +9,4 @@
margin-right: auto; margin-right: auto;
margin-top: 90px; margin-top: 90px;
border: 15px black solid; border: 15px black solid;
} }

View File

@ -9,4 +9,4 @@
margin-right: auto; margin-right: auto;
margin-top: 90px; margin-top: 90px;
border: 15px black solid; border: 15px black solid;
} }

View File

@ -1,8 +1,7 @@
body { body {
margin: 0.5em; margin: 10;
font-family: 'Quicksand', sans-serif; font-family: 'Quicksand', sans-serif;
font-size: 30px; font-size: 18px;
} }
a { a {

View File

@ -1,18 +1,19 @@
#app #main .account #app .account
{ {
background-color: red; background-color: red;
} }
#app #main #app .account, #app .profile
{ {
width: 60%; width: 60%;
display: flex; display: flex;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
flex-direction: column; flex-direction: column;
flex-wrap: wrap;
} }
#app #main .profile #app .profile
{ {
background-color: green; background-color: green;
} }

View File

@ -1,20 +0,0 @@
#app #avatar
{
height: 100px;
width: 100px;
}
#app #username
{
font-size: 0.8em;
}
#app #block {
cursor: pointer;
font-size: 0.7em;
text-decoration: underline;
}
#app {
margin-top: 20px;
}

View File

@ -0,0 +1,11 @@
#app #avatar
{
height: 100px;
width: 100px;
}
#app #username
{
height: 100px;
width: 100px;
}

View File

@ -0,0 +1,11 @@
#app .item img
{
height: 100px;
width: 100px;
}
#app .item a
{
height: 100px;
width: 100px;
}

View File

@ -1,104 +1,17 @@
#app img #app img
{ {
max-height: 3em; max-height: 100px;
max-width: 3em; max-width: 100px;
} }
#app ul #app ul
{ {
font-size: 0.75em; margin: 5px 0 0 0;
margin: 0.25em 0 0 0;
padding: 0 0 0 0; padding: 0 0 0 0;
list-style-type: none; list-style-type: none;
max-height: 80vh;
overflow: auto;
} }
#app li #app li
{ {
margin: 0.25em 0.25em 0 0; margin: 10px 10px 0 0;
}
#app #chats {
overflow: hidden;
display: flex;
text-align: left;
}
#app #users {
margin: 0em 1.0em 0em 0.05em;
}
#app #chat {
position: relative;
max-height: 100vh;
width: 100vh;
/*border: 2px solid green;*/
overflow: hidden;
}
#app #members {
font-size: 1em;
}
#app #add_chat_off {
text-decoration: underline;
cursor: pointer;
}
#app #add_chat_on {
cursor: pointer;
}
#app #messages {
max-height: 60vh;
overflow: scroll;
overflow-y: scroll;
overflow-x: hidden;
font-size: 0.75em;
}
#app #input_user{
color: green;
width: 8.5em;
height: 1.1em;
font-size: 0.65em;
border: none;
outline: none;
border-bottom: 0.15em solid green;
}
#app #input_chat{
position: sticky;
bottom: 0;
/*width: calc(100% - 8px);*/
width: 100%;
border: none;
outline: none;
border-bottom: 0.2em solid green;
caret-color: green;
color: green;
font-size: 16px;
}
#app #you {
text-align: left;
position: relative;
max-width: 48%;
left: 0.5em;
margin: 0.5em 0 0 0;
color: green;
word-wrap: break-word;
}
#app #other {
text-align: right;
position: relative;
max-width: 48%;
margin: 0.5em 0 0 auto;
right: 0.5em;
color: red;
/* permet le retour à la ligne à la place de dépasser*/
word-wrap: break-word;
} }

View File

@ -1,19 +0,0 @@
import { Profile } from "./profile.js";
class MyProfile extends Profile
{
async change_avatar(form_data)
{
let response = await this.client._patch_file(`/api/profiles/me`, form_data);
let response_data = await response.json()
return response_data;
}
async init()
{
super.init("me");
}
}
export {MyProfile}

View File

@ -1,15 +1,7 @@
import { Client } from "./client.js";
class Account class Account
{ {
/**
* @param {Client} client
*/
constructor (client) constructor (client)
{ {
/**
* @type {Client} client
*/
this.client = client; this.client = client;
} }
@ -33,9 +25,10 @@ class Account
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'})) if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
{ {
this.client._update_logged(false); this.client._logged = false;
return null; return null;
} }
console.log(response_data)
if (response_data == "user deleted") if (response_data == "user deleted")
this.client._logged = false; this.client._logged = false;
return response_data; return response_data;
@ -62,7 +55,7 @@ class Account
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'})) if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
{ {
this.client._; this.client._logged = false;
return null; return null;
} }
return response_data; return response_data;

View File

@ -1,80 +1,9 @@
import {Message} from "./message.js"
class Channel { class Channel {
constructor(client, channel_id, members_id, messages, reload) { constructor(id, members, messages) {
this.client = client; this.id = id;
this.channel_id = channel_id; this.members = members;
this.members_id = members_id; this.messages = messages;
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 = `${window.location.protocol[4] === 's' ? 'wss' : '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)
{
console.log(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, receivers_id) {
if (this.chatSocket == undefined)
return;
this.chatSocket.send(JSON.stringify({
'message':message,
'receivers_id':receivers_id,
}));
}
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,47 +1,19 @@
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(members_id, reload) { async createChannel(users_id) {
console.log(users_id);
let null_id = false;
members_id.forEach(member_id => {
if (member_id == null)
null_id = true;
});
if (null_id)
return console.log(members_id, "createChannel error, null id;");
let response = await this.client._post("/api/chat/", { let response = await this.client._post("/api/chat/", {
members_id:members_id users_id:users_id
}); });
let data = await response.json(); let data = await response.json();
let exit_code = await response.status; if (data == "Channel already exist")
if (exit_code >= 300) return null;
return undefined;
let messages = undefined;
if (exit_code == 200)
messages = data.messages;
return new Channel(this.client, data.channel_id, members_id, messages, reload);
}
async deleteChannel(members_id) {
let response = await this.client._delete("/api/chat/", {
members_id:members_id
});
let data = await response.json();
console.log(response.status)
return data; return data;
} }
} }
export {Channels} export {Channels}

View File

@ -1,10 +0,0 @@
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

@ -1,10 +1,7 @@
import { Account } from "./account.js"; import { Account } from "./account.js";
import { MatchMaking } from "./matchmaking.js"; import { MatchMaking } from "./matchmaking.js";
import { Profile } from "./profile.js";
import { Profiles } from "./profiles.js"; import { Profiles } from "./profiles.js";
import { Channels } from './chat/channels.js';
import { MyProfile } from "./MyProfile.js";
import { navigateTo } from "../index.js"
import { Tourmanents } from "./tournament/tournaments.js";
function getCookie(name) function getCookie(name)
{ {
@ -24,11 +21,7 @@ class Client
this.account = new Account(this); this.account = new Account(this);
this.profiles = new Profiles(this); this.profiles = new Profiles(this);
this.matchmaking = new MatchMaking(this); this.matchmaking = new MatchMaking(this);
this.tournaments = new Tourmanents(this);
this._logged = undefined; this._logged = undefined;
this.channels = new Channels(this);
this.channel = undefined;
} }
async isAuthentificate() async isAuthentificate()
@ -38,11 +31,10 @@ class Client
return this.logged; return this.logged;
} }
async _get(uri, data) async _get(uri)
{ {
let response = await fetch(this._url + uri, { let response = await fetch(this._url + uri, {
method: "GET", method: "GET",
body: JSON.stringify(data),
}); });
return response; return response;
} }
@ -98,27 +90,15 @@ class Client
return response; return response;
} }
async _update_logged(state)
{
if (!this.logged && state)
{
this.me = new MyProfile(this);
await this.me.init();
}
if (this.logged && !state)
{
navigateTo("/login");
}
this.logged = state;
}
async login(username, password) async login(username, password)
{ {
let response = await this._post("/api/accounts/login", {username: username, password: password}) let response = await this._post("/api/accounts/login", {username: username, password: password})
let data = await response.json(); let data = await response.json();
if (data.id != undefined) if (data.id != undefined)
{ {
await this._update_logged(true); this.me = new Profile(this)
await this.me.init(data.id)
this.logged = true;
return null; return null;
} }
return data; return data;
@ -127,7 +107,7 @@ class Client
async logout() async logout()
{ {
await this._get("/api/accounts/logout"); await this._get("/api/accounts/logout");
await this._update_logged(false); this.logged = false;
} }
async _test_logged() async _test_logged()
@ -136,7 +116,10 @@ class Client
let data = await response.json(); let data = await response.json();
if (data.id !== undefined) if (data.id !== undefined)
await this._update_logged(true); {
this.me = new Profile(this)
await this.me.init(data.id)
}
return data.id !== undefined; return data.id !== undefined;
} }
} }

View File

@ -1,52 +1,37 @@
import { Client } from "./client.js"; import { client, navigateTo } from "../index.js"
class MatchMaking class MatchMaking
{ {
/** /**
* @param {Client} client * @param {client} client
*/ */
constructor(client) constructor(client)
{ {
/** /**
* @type {Client} * @type {client}
*/ */
this.client = client this.client = client
this.searching = false;
} }
async start(receive_func, disconnect_func, mode) async start(func)
{ {
if (!await this.client.isAuthentificate()) if (!await this.client.isAuthentificate())
return null; return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/matchmaking/${mode}`; let url = `wss://${window.location.host}/ws/matchmaking/`;
this._socket = new WebSocket(url);
this.searching = true; this._chatSocket = new WebSocket(url);
this.receive_func = receive_func; this._chatSocket.onmessage = function (event) {
this.disconnect_func = disconnect_func;
this._socket.onmessage = function (event) {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
receive_func(data); func(data.game_id)
}; };
this._socket.onclose = this.onclose.bind(this);
}
onclose(event)
{
this.stop();
this.disconnect_func(event);
} }
async stop() async stop()
{ {
this.searching = false; this._chatSocket.close()
this._socket.close()
} }
} }
export {MatchMaking} export {MatchMaking}

View File

@ -1,49 +1,35 @@
import { Client } from "./client.js";
class Profile class Profile
{ {
/**
* @param {Client} client
*/
constructor (client, username = undefined, avatar_url = undefined, user_id = undefined) constructor (client, username = undefined, avatar_url = undefined, user_id = undefined)
{ {
/**
* @type {Client} client
*/
this.client = client; this.client = client;
this.username = username; this.username = username;
this.avatar_url = avatar_url; this.avatar_url = avatar_url
this.user_id = user_id; this.user_id = user_id
this.isBlocked = false;
} }
async init(user_id) async init(user_id)
{ {
let response = await this.client._get(`/api/profiles/${user_id}`); let response = await this.client._get(`/api/profiles/${user_id}`);
if (response.status === 404)
return 1;
let response_data = await response.json(); let response_data = await response.json();
this.user_id = response_data.user_id; this.user_id = user_id;
this.username = response_data.username; this.username = response_data.username;
this.avatar_url = response_data.avatar_url; this.avatar_url = response_data.avatar_url;
}
let block_response = await this.client._get("/api/profiles/block"); async change_avatar(form_data)
{
let response = await this.client._patch_file(`/api/profiles/${this.user_id}`, form_data);
let response_data = await response.json()
if (block_response.status == 404) return response_data;
return }
let block_data = await block_response.json(); async setData (data)
let block_list = JSON.parse(block_data); {
block_list.forEach(block => {
let blocker = block.fields.blocker;
let blocked = block.fields.blocked;
if (blocker == this.client.me.user_id && blocked == user_id)
return this.isBlocked = true;
});
} }
} }
export {Profile} export {Profile}

View File

@ -2,14 +2,8 @@ import { Profile } from "./profile.js";
class Profiles class Profiles
{ {
/**
* @param {Client} client
*/
constructor (client) constructor (client)
{ {
/**
* @type {Client} client
*/
this.client = client this.client = client
} }
@ -28,38 +22,9 @@ class Profiles
async getProfile(user_id) async getProfile(user_id)
{ {
let profile = new Profile(this.client); let profile = new Profile(this.client);
if (await profile.init(user_id)) await profile.init(user_id);
return null;
return profile; return profile;
} }
async block(user_id) {
// blocker & blocked
let response = await this.client._post("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
});
let data = await response.json();
console.log(response.status);
console.log(data);
return data;
}
async deblock(user_id) {
// blocker & blocked
let response = await this.client._delete("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
});
let data = await response.json();
console.log(response.status);
console.log(data);
return data;
}
} }
export {Profiles} export {Profiles}

View File

@ -1,98 +0,0 @@
import { Client } from "../client.js";
class Tourmanent
{
/**
* @param {Client} client
*/
constructor(client, name = undefined, nb_players = undefined, nb_players_by_game = undefined, level = undefined, started = undefined, finished = undefined, levels = undefined, id = undefined)
{
/**
* @type {Client}
*/
this.client = client;
this.name = name || `${nb_players_by_game}x1, ${nb_players} players`;
this.nb_players = nb_players;
this.nb_players_by_game = nb_players_by_game;
this.level = level;
this.started = started;
this.finished = finished;
this.levels = levels;
this.state = this.get_state();
this.id = id
this.connected = false;
}
get_state()
{
if (this.finished)
return "finished";
if (this.started)
return "started";
else
return "waiting";
}
async init(id)
{
let response = await this.client._get(`/api/tournaments/${id}`);
if (response.status === 404)
return 1;
let response_data = await response.json();
this.name = response_data.name || `${response_data.nb_players_by_game}x1, ${response_data.nb_players} players`;
this.nb_players = response_data.nb_players;
this.nb_players_by_game = response_data.nb_players_by_game;
this.level = response_data.level;
this.started = response_data.started;
this.finished = response_data.finished;
this.levels = response_data.levels;
this.id = response_data.id
this.state = this.get_state();
}
leave(event)
{
if (this.connected == false)
return
this.connected = false;
this._socket.close()
this.disconnect_func(event);
}
toggle_participation()
{
if (!this.connected)
return
this._socket.send(JSON.stringify({participate: ""}));
}
async join(receive_func, disconnect_func)
{
if (!await this.client.isAuthentificate())
return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/tournaments/${this.id}`;
this._socket = new WebSocket(url);
this.connected = true;
this.isParticipating = false;
this.receive_func = receive_func;
this.disconnect_func = disconnect_func;
this._socket.onmessage = function (event) {
const data = JSON.parse(event.data);
receive_func(data);
};
this._socket.onclose = this.leave.bind(this);
}
}
export { Tourmanent }

View File

@ -1,78 +0,0 @@
import { Client } from "../client.js";
import { Tourmanent } from "./tournament.js";
class Tourmanents
{
/**
* @param {Client} client
*/
constructor(client)
{
/**
* @type {Client}
*/
this.client = client
}
async getTournament(id)
{
let tournament = new Tourmanent(this.client);
if (await tournament.init(id))
return null;
return tournament;
}
async createTournament(nb_players, nb_players_by_game, name = "")
{
let response = await this.client._post("/api/tournaments/", {nb_players: nb_players, nb_players_by_game: nb_players_by_game, name: name});
if (response.status === 403)
{
this.client._update_logged(false);
return null;
}
let response_data = await response.json();
return response_data;
}
/**
* @param {string} state must be "finished", or "started", or "waiting". Any other return all elements
*/
async search(state)
{
let response = await this.client._get(`/api/tournaments/search/${state}`);
let response_data = await response.json()
if (response.status === 404)
{
this.client._update_logged(false);
return null;
}
let tournaments = [];
response_data.forEach(tournament_data => {
tournaments.push(new Tourmanent(this.client,
tournament_data.name,
tournament_data.nb_players,
tournament_data.nb_players_by_game,
tournament_data.level,
tournament_data.started,
tournament_data.finished,
tournament_data.levels,
tournament_data.id));
});
return tournaments;
}
async all()
{
return await this.search("");
}
}
export { Tourmanents }

View File

@ -3,6 +3,7 @@ import { Client } from "./api/client.js";
import LoginView from "./views/accounts/LoginView.js"; import LoginView from "./views/accounts/LoginView.js";
import Dashboard from "./views/Dashboard.js"; import Dashboard from "./views/Dashboard.js";
import Search from "./views/Search.js"; import Search from "./views/Search.js";
import Chat from "./views/Chat.js";
import HomeView from "./views/HomeView.js"; import HomeView from "./views/HomeView.js";
import RegisterView from "./views/accounts/RegisterView.js"; import RegisterView from "./views/accounts/RegisterView.js";
import LogoutView from "./views/accounts/LogoutView.js"; import LogoutView from "./views/accounts/LogoutView.js";
@ -10,13 +11,10 @@ import GameView from "./views/Game.js"
import PageNotFoundView from './views/PageNotFoundView.js' import PageNotFoundView from './views/PageNotFoundView.js'
import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js"; import AbstractRedirectView from "./views/AbstractRedirectView.js";
import MeView from "./views/MeView.js"; import MeView from "./views/MeView.js";
import ProfilePageView from "./views/ProfilePageView.js"; import ProfilePageView from "./views/profiles/ProfilePageView.js";
import MatchMakingView from "./views/MatchMakingView.js"; import MatchMakingView from "./views/MatchMakingView.js";
import TournamentPageView from "./views/TournamentPageView.js";
import TournamentsView from "./views/TournamentsListView.js";
import TournamentCreateView from "./views/TournamentCreateView.js";
let client = new Client(location.protocol + "//" + location.host) let client = new Client(location.protocol + "//" + location.host)
@ -38,30 +36,15 @@ const navigateTo = async (uri) => {
history.pushState(null, null, uri); history.pushState(null, null, uri);
}; };
async function renderView(view)
{
let content = await view.getHtml();
if (content == null)
return 1;
view.setTitle();
document.querySelector("#app").innerHTML = content
if (await view.postInit())
renderView(new PageNotFoundView());
}
const router = async (uri) => { const router = async (uri) => {
const routes = [ const routes = [
{ path: "/", view: Dashboard }, { path: "/", view: Dashboard },
{ path: "/profiles/:id", view: ProfilePageView }, { path: "/profiles/:id", view: ProfilePageView },
{ path: "/tournaments/create", view: TournamentCreateView },
{ path: "/tournaments/:id", view: TournamentPageView },
{ path: "/tournaments/", view: TournamentsView },
{ path: "/login", view: LoginView }, { path: "/login", view: LoginView },
{ path: "/logout", view: LogoutView }, { path: "/logout", view: LogoutView },
{ path: "/register", view: RegisterView }, { path: "/register", view: RegisterView },
{ path: "/search", view: Search }, { path: "/search", view: Search },
{ path: "/chat", view: Chat },
{ path: "/home", view: HomeView }, { path: "/home", view: HomeView },
{ path: "/me", view: MeView }, { path: "/me", view: MeView },
{ path: "/matchmaking", view: MatchMakingView }, { path: "/matchmaking", view: MatchMakingView },
@ -98,8 +81,14 @@ const router = async (uri) => {
lastView = view; lastView = view;
await client.isAuthentificate(); let content = await view.getHtml();
renderView(view); if (content == null)
return 1;
view.setTitle();
document.querySelector("#app").innerHTML = content
await view.postInit();
return 0; return 0;
}; };

View File

@ -1,18 +0,0 @@
function clear(property_name, elements_id)
{
elements_id.forEach(element_id => {
let element = document.getElementById(element_id)
element[property_name] = ""
});
}
function fill_errors(errors, property_name)
{
Object.keys(errors).forEach(error_field =>
{
let element = document.getElementById(error_field);
element[property_name] = errors[error_field];
});
}
export {fill_errors, clear}

View File

@ -1,4 +1,4 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js"; import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{ export default class extends AbstractRedirectView{

View File

@ -1,4 +1,4 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js"; import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{ export default class extends AbstractRedirectView{

View File

@ -1,4 +1,4 @@
import { navigateTo } from "../../index.js"; import { navigateTo } from "../index.js";
import AbstractView from "./AbstractView.js"; import AbstractView from "./AbstractView.js";
export default class extends AbstractView{ export default class extends AbstractView{

View File

@ -0,0 +1,56 @@
import AbstractView from "./AbstractView.js";
export default class extends AbstractView {
constructor(params) {
super(params, "Chat");
let url = `ws://${window.location.host}/ws/socket-server/`
this.chatSocket = new WebSocket(url)
this.chatSocket.onmessage = function(e){
let data = JSON.parse(e.data)
console.log('Data:', data)
if (data.type === 'chat') {
let messages = document.getElementById('messages')
let username = data.username === null || data.username.length <= 0 ? "NoName" : data.username;
messages.insertAdjacentHTML('beforeend', `
<div><p>${username}: ${data.message}</p></div>
`)
}
}
}
async postInit() {
let form = document.getElementById('form')
form.addEventListener('submit', (e)=> {
e.preventDefault()
let message = e.target.message.value
this.chatSocket.send(JSON.stringify({
'message':message
}))
form.reset()
})
}
async leavePage() {
this.chatSocket.close();
}
async getHtml() {
return `
<h1>Chat</h1>
<form id="form">
<input type="text" name="message" placeholder="message"/>
</form>
<div id="messages">
</div>
`;
}
}

View File

@ -1,4 +1,4 @@
import AbstractView from "./abstracts/AbstractView.js"; import AbstractView from "./AbstractView.js";
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {
@ -16,4 +16,4 @@ export default class extends AbstractView {
</p> </p>
`; `;
} }
} }

View File

@ -1,4 +1,4 @@
import AbstractView from "./abstracts/AbstractView.js"; import AbstractView from './AbstractView.js'
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {

View File

@ -1,4 +1,4 @@
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js"; import AbstractAuthentificateView from "./AbstractAuthentifiedView.js";
export default class extends AbstractAuthentificateView { export default class extends AbstractAuthentificateView {
constructor(params) { constructor(params) {
@ -15,4 +15,4 @@ export default class extends AbstractAuthentificateView {
<a href="/logout" class="nav__link" data-link>Logout</a> <a href="/logout" class="nav__link" data-link>Logout</a>
`; `;
} }
} }

View File

@ -1,64 +1,24 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js"; import AbstractView from "./AbstractView.js";
import AbstractAuthentifiedView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView { function game_found(game_id)
constructor(params) {
{ navigateTo(`/games/${game_id}`)
super(params, "Matchmaking"); }
}
async press_button() export default class extends AbstractView {
{ constructor(params) {
if (client.matchmaking.searching) super(params, "Dashboard");
{
client.matchmaking.stop();
document.getElementById("button").value = "Find a game"
}
else
{
let nb_players = document.getElementById("nb_players-input").value
await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), nb_players);
document.getElementById("button").value = "Stop matchmaking"
}
}
ondisconnect(event)
{
if (event.code === 1000)
clear("innerText", ["detail"])
document.getElementById("button").value = "Find a game"
}
onreceive(data)
{
if (data.detail === "game_found")
{
navigateTo(`/games/${data.game_id}`);
return;
}
this.display_data(data)
}
display_data(data)
{
clear("innerText", ["detail"]);
fill_errors(data, "innerText");
} }
async postInit() async postInit()
{ {
document.getElementById("button").onclick = this.press_button.bind(this) await client.matchmaking.start(game_found)
} }
async getHtml() { async getHtml() {
return ` return `
<h1>Select mode</h1> <h1>finding<h1>
<input type="number" value="2" id="nb_players-input">
<input type="button" value="Find a game" id="button">
<span id="detail"></span>
`; `;
} }
@ -66,4 +26,4 @@ export default class extends AbstractAuthentifiedView {
{ {
await client.matchmaking.stop(); await client.matchmaking.stop();
} }
} }

View File

@ -1,6 +1,5 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js"; import AbstractAuthentificateView from "./AbstractAuthentifiedView.js";
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentificateView export default class extends AbstractAuthentificateView
{ {
@ -11,40 +10,29 @@ export default class extends AbstractAuthentificateView
async postInit() async postInit()
{ {
document.getElementById("save-account-button").onclick = this.save_account; if (this.fill() === null)
document.getElementById("delete-account-button").onclick = this.delete_account; return;
document.getElementById("save-profile-button").onclick = this.save_profile; document.getElementById("save-account-button").onclick = this.acccount_save;
document.getElementById("delete-account-button").onclick = this.account_delete_accounts;
} }
async delete_account() async fill()
{ {
let current_password = document.getElementById("current_password-input").value; let data = await client.account.get();
let response_data = await client.account.delete(current_password); if (data === null)
console.log(await client.isAuthentificate())
if (response_data === null || response_data === "user deleted")
{ {
navigateTo("/login"); navigateTo("/login")
return; return;
} }
clear("innerHTML", ["current_password-input"]) document.getElementById("username").value = data.username;
fill_errors({"current_password-input": response_data["password"]}, "innerHTML")
} }
async save_account() async delete_accounts()
{ {
let username = document.getElementById("username-input").value; let current_password = document.getElementById("current_password").value;
let new_password = document.getElementById("new_password-input").value;
let current_password = document.getElementById("current_password-input").value; let response_data = await client.account.delete(current_password);
let data = {};
data.username = username;
if (new_password.length != 0)
data.new_password = new_password;
let response_data = await client.account.update(data, current_password);
if (response_data === null) if (response_data === null)
{ {
@ -52,54 +40,92 @@ export default class extends AbstractAuthentificateView
return; return;
} }
if (response_data === "data has been alterate") ["delete", "current_password"].forEach(error_field => {
response_data = {"save-account": "saved"} let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]) if (response_data === "user deleted")
fill_errors(response_data, "innerHTML") {
document.getElementById(`error_delete`).innerHTML = "OK";
navigateTo("/login")
return;
}
document.getElementById("error_current_password").innerHTML = response_data["password"]
} }
async save_profile() async save()
{ {
let avatar = document.getElementById("avatar-input"); let username = document.getElementById("username").value;
let new_password = document.getElementById("new_password").value;
let current_password = document.getElementById("current_password").value;
let data = {};
data.username = username;
if (new_password.length != 0)
data.new_password = new_password;
let response_data = await client.account.update(data, current_password);
if (response_data === null)
{
navigateTo("/login");
return;
}
else if (response_data === "data has been alterate")
{
navigateTo("/me");
return;
}
["username", "new_password", "current_password"].forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
let avatar = document.getElementById("avatar");
if (avatar.files[0] !== undefined) if (avatar.files[0] !== undefined)
{ {
let form_data = new FormData(); let form_data = new FormData();
form_data.append("file", avatar.files[0]); form_data.append("file", avatar.files[0]);
await client.me.change_avatar(form_data); await client.me.change_avatar(form_data)
} }
document.getElementById("save-profile").innerHTML = "Saved";
} }
async getHtml() async getHtml()
{ {
return ` return `
<link rel="stylesheet" href="/static/css/me.css"> <link rel="stylesheet" href="static/css/me.css">
<h1>ME</h1> <h1>ME</h1>
<div id="main"> <div class="account">
<div class="account"> <h3>Account</h3>
<h3>Account</h3> <input type="text" placeholder="username" id="username">
<input type="text" placeholder="username" id="username-input" text=${client.me.username}> <span id="error_username"></span>
<span id="username"></span> <input type=password placeholder="new password" id="new_password">
<input type=password placeholder="new_password" id="new_password-input"> <span id="error_new_password"></span>
<span id="new_password"></span> <input type=password placeholder="current password" id="current_password">
<input type=password placeholder="current_password" id="current_password-input"> <span id="error_current_password"></span>
<span id="current_password"></span> <input type="button" value="Save Credentials" id="save-account-button">
<span id="error_save"></span>
<input type="button" value="Save Credentials" id="save-account-button"> <input type="button" value="Delete Account" id="delete-account-button">
<span id="save-account"></span> <span id="error_delete"></span>
<input type="button" value="Delete Account" id="delete-account-button">
<span id="delete-account"></span>
</div>
<div class="profile">
<h3>Profile</h3>
<input type="file" id="avatar-input" accept="image/png, image/jpeg">
<input type="button" value="Save profile" id="save-profile-button">
<span id="save-profile"></span>
</div>
<a href="/logout" class="nav__link" data-link>Logout</a>
</div> </div>
<div class="profile">
<h3>Profile</h3>
<input type="file" id="avatar" accept="image/png, image/jpeg">
<input type="button" value="Save profile" id="save-profile-button">
<span id="error_save"></span>
</div>
<a href="/logout" class="nav__link" data-link>Logout</a>
`; `;
} }
} }

View File

@ -1,4 +1,4 @@
import AbstractView from "./abstracts/AbstractView.js"; import AbstractView from "./AbstractView.js";
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {

View File

@ -1,67 +0,0 @@
import AbstractView from "./abstracts/AbstractView.js";
import { client } from "../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Profile ");
this.user_id = params.id;
}
async postInit()
{
let profile = await client.profiles.getProfile(this.user_id);
if (profile === null)
return 1;
this.profile = await client.profiles.getProfile(this.user_id);
this.info = document.getElementById("info");
// Username
let username = document.createElement("a");
username.id = "username";
username.appendChild(document.createTextNode(this.profile.username));
this.info.appendChild(username);
this.info.appendChild(document.createElement("br"));
// Avatar
let avatar = document.createElement("img");
avatar.id = "avatar";
avatar.src = this.profile.avatar_url;
this.info.appendChild(avatar);
await this.blockButton();
}
async blockButton() {
// Block option
if (client.me.user_id != this.user_id) {
let block = document.getElementById("block") || document.createElement("a");
block.id = "block";
block.innerText = "";
block.onclick = async () => {
if (!this.profile.isBlocked)
await client.profiles.block(this.user_id);
else
await client.profiles.deblock(this.user_id);
this.profile = await client.profiles.getProfile(this.user_id);
this.blockButton();
};
if (this.profile.isBlocked)
block.appendChild(document.createTextNode("Deblock"));
else
block.appendChild(document.createTextNode("Block"));
this.info.appendChild(block);
}
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/profile.css">
<div id="info">
</div>
`;
}
}

View File

@ -1,6 +1,5 @@
import AbstractView from "./abstracts/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) {
@ -9,21 +8,16 @@ export default class extends AbstractView {
async postInit() { async postInit() {
let search = document.getElementById("input_user"); let search = document.getElementById("form");
search.oninput = this.users; search.addEventListener("input", this.users)
let chat_input = document.getElementById("input_chat");
//chat_input.addEventListener("keydown", this.chat_manager)
this.last_add_chat = undefined;
this.users(); this.users();
this.chat();
} }
async users() { async users() {
let search = document.getElementById("input_user").value.toLowerCase(); let search = document.getElementById("form").value;
let logged = await client.isAuthentificate(); let logged = await client.isAuthentificate();
@ -31,13 +25,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.toLowerCase().startsWith(search) == true).forEach((user) => { users.filter(user => user.username.startsWith(search) == true).forEach((user) => {
if (user.user_id == null) {
console.log("list User one with id null;");
return;
}
var new_user = document.createElement("li"); var new_user = document.createElement("li");
// username // username
@ -50,37 +38,20 @@ export default class extends AbstractView {
new_user.appendChild(document.createTextNode(" ")); new_user.appendChild(document.createTextNode(" "));
// chat // chat
if (logged && client.me.user_id != user.user_id) { if (logged) {
let add_chat = document.createElement("a"); let chat = document.createElement("a");
add_chat.id = "add_chat_off"; let array = [
add_chat.onclick = 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]);
if (client.channel == undefined) { });
add_chat.id = "add_chat_off"; //chat.href = `/chat`
this.last_add_chat = undefined; chat.appendChild(document.createTextNode("Chat"));
return this.hideChat(); new_user.appendChild(chat);
}
client.channel.disconnect();
}
client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat);
this.chat();
if (this.last_add_chat != undefined)
this.last_add_chat.id = "add_chat_off";
this.last_add_chat = add_chat;
add_chat.id = "add_chat_on";
};
add_chat.appendChild(document.createTextNode("Chat"));
new_user.appendChild(add_chat);
new_user.appendChild(document.createTextNode(" "));
} }
// break line // break line
@ -98,130 +69,17 @@ export default class extends AbstractView {
} }
async chat() {
let users = await client.profiles.all();
let logged = await client.isAuthentificate();
/*let reload = document.getElementById("messages");
if (reload != null)
reload.remove();*/
let 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.getElementById("messages");
if (messages == null) {
messages = document.createElement("div");
messages.id = "messages";
if (document.getElementById("input_chat") == null)
chat.appendChild(messages);
else
document.getElementById("input_chat").before(messages);
}
// les messages, réecriture seulement du dernier
let i = 0;
client.channel.messages.forEach((message) => {
if (messages.children[i] == null || message.content != messages.children[i].innerText) {
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);
}
i++;
});
// 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.onkeydown = async () => {
if (event.keyCode == 13 && client.channel != undefined) {
//let chat_input = document.getElementById("input_chat");
let chat_text = chat_input.value;
let receivers_id = [];
client.channel.members_id.forEach((member_id) => {
if (member_id != client.me.user_id)
receivers_id.push(users.filter(user => user.user_id == member_id)[0].user_id);
});
await client.channel.sendMessageChannel(chat_text, receivers_id)
// Reset
chat_input.value = "";
}
};
}
// nom des membres du chat
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);
// Scroll to the bottom of messages
messages.scrollTop = messages.scrollHeight;
}
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">
<div id="chats"> <input id="form" type="text" name="message" placeholder="userbozo"/>
<div id="users">
<input id="input_user" type="text" name="message" placeholder="userbozo"/> <div id="users">
<ul id="list_users"> <ul id="list_users">
</ul> </ul>
</div>
</div> </div>
`; `;
} }
} }

View File

@ -1,52 +0,0 @@
import {client, navigateTo} from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthentifiedView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView
{
constructor(params)
{
super(params, "Create tournament");
this.id = params.id;
}
async create()
{
let name = document.getElementById("name-input").value;
let nb_players = document.getElementById("nb_players-input").value;
let nb_players_by_game = document.getElementById("nb_players_by_game-input").value
let response_data = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
if (response_data === null)
return;
let id = response_data["id"]
if (id !== undefined)
{
navigateTo(`/tournaments/${id}`);
return;
}
clear("innerHTML", ["name", "nb_players", "nb_players_by_game"]);
fill_errors(response_data, "innerHTML");
}
async postInit()
{
document.getElementById("create-button").onclick = this.create;
}
async getHtml()
{
return `
<input type="text" id="name-input" placeholder="Tournament name">
<span id="name"></span>
<input type="number" id="nb_players-input" placeholder="Number of players in tournament">
<span id="nb_players"></span>
<input type="number" id="nb_players_by_game-input" placeholder="Number of players by game">
<span id="nb_players_by_game"></span>
<input type="button" id="create-button" value="Create tournament">
`
}
}

View File

@ -1,98 +0,0 @@
import {client, navigateTo} from "../index.js";
import AbstractAuthentifiedView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView
{
constructor(params)
{
super(params, "Tournament");
this.id = params.id;
}
pressButton()
{
this.tournament.toggle_participation();
}
async receive(data)
{
if (data.detail === "nb_participants" || data.detail === "update_participants")
document.getElementById("nb_participants").innerText = `${data.nb_participants} / ${this.tournament.nb_players}`
if (data.detail === "go_to")
navigateTo(data.url);
if (data.detail === "is_participant")
this.updateParticipating(data.is_participant)
if (data.detail === "error")
document.getElementById("display").innerText = data.error_message
}
async updateParticipating(state)
{
document.getElementById("button").value = state ? `Leave ${this.tournament.name}` : `Join ${this.tournament.name}`;
document.getElementById("display").innerText = state ? "You are a particpant" : "You are not a participant";
}
async ondisconnect(event)
{
}
async postInit()
{
this.tournament = await client.tournaments.getTournament(this.id);
if (this.tournament === null)
return 1;
this.tournament.join(this.receive.bind(this), this.ondisconnect.bind(this));
let button = document.getElementById("button")
button.onclick = this.pressButton.bind(this);
document.getElementById("name").innerText = this.tournament.name;
document.getElementById("nb_players").innerText = this.tournament.nb_players;
document.getElementById("nb_players_by_game").innerText = this.tournament.nb_players_by_game;
document.getElementById("level").innerText = this.tournament.level;
document.getElementById("state").innerText = this.tournament.state;
if (this.tournament.state === "waiting")
button.disabled = false;
}
async getHtml()
{
return `
<table>
<thead>
<tr>
<th id="name">Loading...</th>
</tr>
</thead>
<tbody>
<tr>
<td>Number of players</td>
<td id="nb_players">Loading...</td>
</tr>
<tr>
<td>Number of players by game</td>
<td id="nb_players_by_game">Loading...</td>
</tr>
<tr>
<td>Number of round</td>
<td id="level">Loading...</td>
</tr>
<tr>
<td>Number of player</td>
<td id="nb_participants">Loading...</td>
</tr>
<tr>
<td>status</td>
<td id="state">Loading...</td>
</tr>
</tbody>
</table>
<input type="button" id="button" value="Join tournament" disabled>
<span id="display"></span>
`
}
}

View File

@ -1,133 +0,0 @@
import {client} from "../index.js";
import AbstractAuthentifiedView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView
{
constructor(params)
{
super(params, "Tournament");
this.id = params.id;
}
async external_search()
{
let state = document.getElementById("state-select").value;
this.tournaments = await client.tournaments.search(state);
}
add_nb_player_by_game_selector()
{
let nb_players_by_game_list = new Set()
this.tournaments.forEach(tournament => {
nb_players_by_game_list.add(tournament.nb_players_by_game);
});
let select = document.getElementById("nb-players-by-game-select");
let new_children = []
const opt = document.createElement("option");
opt.value = "all";
opt.text = "All";
new_children.push(opt);
nb_players_by_game_list.forEach(nb_players_by_game => {
const opt = document.createElement("option");
opt.value = nb_players_by_game;
opt.text = nb_players_by_game;
new_children.push(opt);
})
select.replaceChildren(...new_children);
}
internal_search()
{
let nb_players_by_game = document.getElementById("nb-players-by-game-select").value;
this.display_tournaments = [];
this.tournaments.forEach(tournament => {
if (nb_players_by_game === "all" || nb_players_by_game == tournament.nb_players_by_game)
this.display_tournaments.push(tournament);
});
}
display_result()
{
const tournaments_list = document.getElementById("tournaments-list");
const new_children = []
this.display_tournaments.forEach(tournament => {
let tr = document.createElement("tr");
// name
let td = document.createElement("td");
td.innerText = tournament.name;
tr.appendChild(td);
// state
td = document.createElement("td");
td.innerText = tournament.state;
tr.appendChild(td);
// nb_players
td = document.createElement("td");
td.innerText = tournament.nb_players;
tr.appendChild(td);
// nb_players_by_game
td = document.createElement("td");
td.innerText = tournament.nb_players_by_game;
tr.appendChild(td);
new_children.push(tr);
});
tournaments_list.replaceChildren(...new_children);
}
async update_query()
{
this.internal_search();
this.display_result();
}
async update_search()
{
await this.external_search();
this.add_nb_player_by_game_selector();
this.update_query();
}
async postInit()
{
await this.update_search()
document.getElementById("state-select").onchange = this.update_search.bind(this);
document.getElementById("nb-players-by-game-select").onchange = this.update_query.bind(this);
}
async getHtml()
{
return `
<select id="state-select">
<option value="waiting">Waiting</option>
<option value="started">Started</option>
<option value="finished">Finished</option>
<option value="all">All</option>
</select>
<select id="nb-players-by-game-select">
</select>
<table>
<thead>
<td>Name</td>
<td>Status</td>
<td>Max numbers of players</td>
<td>Max numbers of players by game</td>
</thead>
<tbody id="tournaments-list">
</tbody>
</table>
`
}
}

View File

@ -1,11 +1,10 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js"; import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function login() async function login()
{ {
let username = document.getElementById("username-input").value; let username = document.getElementById("username").value;
let password = document.getElementById("password-input").value; let password = document.getElementById("password").value;
let response_data = await client.login(username, password); let response_data = await client.login(username, password);
@ -15,8 +14,17 @@ async function login()
return; return;
} }
clear("innerHTML", ["username", "user", "password"]); ["username", "user", "password"].forEach(error_field => {
fill_errors(response_data, "innerHTML"); let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
} }
export default class extends AbstractNonAuthentifiedView { export default class extends AbstractNonAuthentifiedView {
@ -26,22 +34,22 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("login-button").onclick = login; document.getElementById("button").onclick = login;
} }
async getHtml() { async getHtml() {
return ` return `
<div class=form> <div class=form>
<label>Login</label> <label>Login</label>
<link rel="stylesheet" href="/static/css/accounts/login.css"> <link rel="stylesheet" href="static/css/accounts/login.css">
<input type="text" id="username-input" placeholder="username"> <input type="text" id="username" placeholder="username">
<span id="username"></span> <span id="error_username"></span>
<input type="password" id="password-input" placeholder="password"> <input type="password" id="password" placeholder="password">
<span id="password"></span> <span id="error_password"></span>
<input type="button" value="Login" id="login-button"> <input type="button" value="login" id="button">
<span id="user"></span> <span id="error_user"></span>
<a href="/register" class="nav__link" data-link>Register</a> <a href="/register" class="nav__link" data-link>Register</a>
</div> </div>
`; `;
} }
} }

View File

@ -1,5 +1,5 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js"; import AbstractAuthentifiedView from "../AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView export default class extends AbstractAuthentifiedView
{ {
@ -8,4 +8,4 @@ export default class extends AbstractAuthentifiedView
client.logout(); client.logout();
navigateTo("/login") navigateTo("/login")
} }
} }

View File

@ -1,11 +1,10 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js"; import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function register() async function register()
{ {
let username = document.getElementById("username-input").value; let username = document.getElementById("username").value;
let password = document.getElementById("password-input").value; let password = document.getElementById("password").value;
let response_data = await client.account.create(username, password); let response_data = await client.account.create(username, password);
@ -15,8 +14,17 @@ async function register()
return; return;
} }
clear("innerHTML", ["username", "user", "password"]); ["username", "user", "password"].forEach(error_field => {
fill_errors(response_data, "innerHTML"); let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
} }
export default class extends AbstractNonAuthentifiedView { export default class extends AbstractNonAuthentifiedView {
@ -26,20 +34,20 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("register-button").onclick = register; document.getElementById("button").onclick = register;
} }
async getHtml() { async getHtml() {
return ` return `
<div class=form> <div class=form>
<label>Register</label> <label>Register</label>
<link rel="stylesheet" href="/static/css/accounts/register.css"> <link rel="stylesheet" href="static/css/accounts/register.css">
<input type="text" id="username-input" placeholder="username"> <input type="text" id="username" placeholder="username">
<span id="username"></span> <span id="error_username"></span>
<input type="password" id="password-input" placeholder="password"> <input type="password" id="password" placeholder="password">
<span id="password"></span> <span id="error_password"></span>
<input type="button" value="Register" id="register-button"> <input type="button" value="register" id="button">
<span id="user"></span> <span id="error_user"></span>
<a href="/login" class="nav__link" data-link>Login</a> <a href="/login" class="nav__link" data-link>Login</a>
</div> </div>
`; `;

View File

@ -0,0 +1,29 @@
import AbstractView from "../AbstractView.js";
import { client } from "../../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Profile ");
this.user_id = params.id;
}
async postInit()
{
let profile = await client.profiles.getProfile(this.user_id);
let username_element = document.getElementById("username");
username_element.href = `/profiles/${this.user_id}`;
username_element.appendChild(document.createTextNode(profile.username));
let avatar_element = document.getElementById("avatar");
avatar_element.src = profile.avatar_url;
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/profiles/profile.css">
<img id="avatar"/>
<a id="username"></a>
`;
}
}

View File

@ -1,9 +0,0 @@
PADDLE_SPEED_MAX = 1
PADDLE_SIZE_HEIGHT = 10
PADDLE_SIZE_WIDTH = 100
PADDLE_RAIL = PADDLE_SIZE_WIDTH * 5
BALL_SPEED_INC = 1
BALL_SPEED_START = 1
BALL_SIZE = 4

View File

@ -1,42 +0,0 @@
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from games.models import GameModel
import json
from .models import game_room_manager
class GameWebSocket(AsyncWebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.channel_name = "games"
self.group_name = "games"
def connect(self):
self.user: User = self.scope["user"]
if (self.user.is_anonymous or not self.user.is_authenticated):
return
self.channel_layer.group_add(self.group_name, self.channel_name)
self.game_id = int(self.scope['url_route']['kwargs']['game_id'])
self.room = game_room_manager.get(self.game_id)
if (self.room is None):
self.member.send("Tournament not found")
self.disconnect(1017)
self.room.append(self.member)
def receive(self, text_data: str = None, bytes_data: bytes = None):
self.member.receive(text_data, bytes_data)
def disconnect(self, close_code):
member = self.room.get_member_by_socket(self)
if (member is not None):
self.room.remove(self.member, close_code)

View File

@ -1,27 +1,14 @@
from django.db import models from django.db import models
from .objects.GameRoomManager import GameRoomManager
from channels.generic.websocket import AsyncWebsocketConsumer
# Create your models here. # Create your models here.
class GameModel(models.Model): class GameModel(models.Model):
finished = models.BooleanField(default = False)
started = models.BooleanField(default = False)
winner_id = models.IntegerField(default = -1)
def create(self, players_id: [int]): def create(self, users_id: [int]):
self.save() self.save()
for player_id in players_id: for user_id in users_id:
GameMembersModel(game_id = self.pk, player_id = player_id).save() GameMembersModel(game_id=self.pk, member_id=user_id)
return self.pk return self.pk
def get_players_id(self):
return [game_member.member_id for game_member in GameMembersModel.objects.filter(game_id = self.pk)]
class GameMembersModel(models.Model): class GameMembersModel(models.Model):
game_id = models.IntegerField() game_id = models.IntegerField()
player_id = models.IntegerField() member_id = models.IntegerField()
game_room_manager: GameRoomManager = GameRoomManager()

View File

@ -1,17 +0,0 @@
from channels.generic.websocket import AsyncWebsocketConsumer
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
class GameMember(AbstractRoomMember):
def __init__(self, user_id: int, socket: AsyncWebsocketConsumer):
super().__init__(user_id, socket)
self.is_a_player = False
def receive(self, data: dict):
if (not self.is_a_player):
self.send("You are not a player.")
return
def send_ball(self, ball):
pass

View File

@ -1,9 +0,0 @@
from transcendence.abstract.AbstractRoom import AbstractRoom
from .GameRoomManager import GameRoomManager
class GameRoom(AbstractRoom):
def __init__(self, game_room_manager: GameRoomManager, game_id: int):
super().__init__(game_room_manager)
self.game_id = game_id

View File

@ -1,18 +0,0 @@
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
from ..models import GameModel
class GameRoomManager(AbstractRoomManager):
def get(self, game_id: int):
for room in self._room_list:
if (room.game_id == game_id):
return room
if (GameModel.objects.filter(pk = tournament_id).exists()):
room = TournamentRoom(self, game_id)
self.append(room)
return room
return None

View File

@ -1,6 +0,0 @@
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/tournaments/(?P<tournament_id>\d+)$', consumers.TournamentWebConsumer.as_asgi())
]

View File

@ -1,25 +0,0 @@
from rest_framework import serializers
from .models import GameModel, GameMembersModel
class GameSerializer(serializers.ModelSerializer):
players_id = serializers.SerializerMethodField()
winner_id = serializers.ReadOnlyField()
state = serializers.SerializerMethodField()
started = serializers.ReadOnlyField()
finished = serializers.ReadOnlyField()
class Meta:
model = GameModel
fields = ["id", "winner_id", "state", "started", "finished", "players_id"]
def get_state(self, instance: GameModel):
if (instance.finished):
return "finished"
if (instance.started):
return "started"
return "waiting"
def get_players_id(self, instance: GameModel):
players_id = [player_game.player_id for player_game in GameMembersModel.objects.filter(game_id = instance.pk)]
return players_id

View File

@ -1,11 +0,0 @@
from django.urls import path, re_path
from django.conf import settings
from django.conf.urls.static import static
from .viewset import GameViewSet
from .views import GameConfigView
urlpatterns = [
path("<int:pk>", GameViewSet.as_view({"get": "retrieve"}), name="game_page"),
path("", GameConfigView.as_view(), name = "game_config")
]

View File

@ -1,23 +1,3 @@
from rest_framework.views import APIView from django.shortcuts import render
from rest_framework.response import Response
from rest_framework import permissions, status
from django.http import HttpRequest # Create your views here.
from . import config
class GameConfigView(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request):
config_data = {
"BALL_SIZE": config.BALL_SIZE,
"PADDLE_SPEED_MAX": config.PADDLE_SPEED_MAX,
"PADDLE_SIZE_HEIGHT": config.PADDLE_SIZE_HEIGHT,
"PADDLE_SIZE_WIDTH": config.PADDLE_SIZE_WIDTH,
"PADDLE_RAIL": config.PADDLE_RAIL,
"BALL_SPEED_INC": config.BALL_SPEED_INC,
"BALL_SPEED_START": config.BALL_SPEED_START
}
return Response(config_data, status = status.HTTP_200_OK)

View File

@ -1,27 +0,0 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import permissions, status
from rest_framework.authentication import SessionAuthentication
from django.http import HttpRequest
from django.db.models import QuerySet
from .models import GameModel
from .serializers import GameSerializer
# Create your views here.
class GameViewSet(viewsets.ModelViewSet):
queryset = GameModel.objects
serializer_class = GameSerializer
permission_classes = (permissions.AllowAny,)
authentication_classes = (SessionAuthentication,)
def retrieve(self, request: HttpRequest, pk):
if (not self.queryset.filter(pk = pk).exists()):
return Response({"detail": "Game not found."}, status=status.HTTP_404_NOT_FOUND)
game = self.queryset.get(pk=pk)
return Response(self.serializer_class(game).data, status=status.HTTP_200_OK)

View File

@ -6,7 +6,7 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'transcendence.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'trancendence.settings')
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

View File

@ -6,7 +6,8 @@ from games.models import GameModel
import json import json
from .models import Waiter, WaitingRoom, WaitingRoomManager, normal queue_id: [int] = []
queue_ws: [WebsocketConsumer] = []
class MatchMaking(WebsocketConsumer): class MatchMaking(WebsocketConsumer):
@ -23,19 +24,25 @@ class MatchMaking(WebsocketConsumer):
self.channel_layer.group_add(self.group_name, self.channel_name) self.channel_layer.group_add(self.group_name, self.channel_name)
self.mode = int(self.scope['url_route']['kwargs']['mode']) self.accept()
self.group_name = self.mode
waiting_room: WaitingRoom = normal.get(self.mode) global queue_id, queue_ws
waiting_room.append(Waiter(user.pk, self)) queue_id.append(user.pk)
waiting_room.broadcast(f"{len(waiting_room)} / {waiting_room.mode}") queue_ws.append(self)
if (len(waiting_room) == waiting_room.mode):
game_id: int = GameModel().create(waiting_room.get_users_id())
waiting_room.broadcast("game_found", {"game_id": game_id})
waiting_room.clear()
if len(set(queue_id)) == 2:
game_id: int = GameModel().create(set(queue_id))
event = {"game_id": game_id}
for ws in queue_ws:
ws.send(text_data=json.dumps({'game_id': game_id}))
queue_id.clear()
queue_ws.clear()
def disconnect(self, close_code): def disconnect(self, close_code):
waiting_room: WaitingRoom = normal.get(self.mode) user: User = self.scope["user"]
waiter: Waiter = waiting_room.get_member_by_socket(self) global queue_id, queue_ws
if (waiter is not None): if (user.pk in queue_id):
waiting_room.remove(waiter, 1016) queue_ws.pop(queue_id.index(user.pk))
queue_id.remove(user.pk)
self.channel_layer.group_discard(self.group_name, self.channel_name)

View File

@ -1,39 +1,3 @@
from django.db import models from django.db import models
from channels.generic.websocket import WebsocketConsumer
import json
from transcendence.abstract.AbstractRoom import AbstractRoom
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
# Create your models here. # Create your models here.
class Waiter(AbstractRoomMember):
pass
class WaitingRoom(AbstractRoom):
def __init__(self, room_manager,mode):
super().__init__(room_manager)
self.mode = mode
def append(self, waiter: Waiter):
tmp: Waiter = self.get_member_by_user_id(waiter.user_id)
if (tmp is not None):
tmp.send("Connection close: Another connection open with the same user id.")
self.remove(tmp)
waiter.accept()
self._member_list.append(waiter)
class WaitingRoomManager(AbstractRoomManager):
def get(self, mode: int):
for waiting_room in self._room_list:
waiting_room: WaitingRoom
if (waiting_room.mode == mode):
return waiting_room
tmp: WaitingRoom = WaitingRoom(self, mode)
super().append(tmp)
return tmp
normal: WaitingRoomManager = WaitingRoomManager()

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/matchmaking/(?P<mode>\d+)$', consumers.MatchMaking.as_asgi()) re_path(r'ws/matchmaking/', consumers.MatchMaking.as_asgi())
] ]

View File

@ -1,7 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import ProfileModel, BlockModel from .models import ProfileModel
# Register your models here. # Register your models here.
admin.site.register(ProfileModel) admin.site.register(ProfileModel)
admin.site.register(BlockModel)

View File

@ -4,7 +4,6 @@ from django.contrib.auth.models import User
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.conf import settings from django.conf import settings
from django.db.models import IntegerField
def upload_to(instance, filename: str): def upload_to(instance, filename: str):
return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}" return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}"
@ -18,11 +17,4 @@ class ProfileModel(models.Model):
def on_user_created(sender, instance, created, **kwargs): def on_user_created(sender, instance, created, **kwargs):
if created: if created:
profile: ProfileModel = ProfileModel.objects.create(pk = instance.pk, user = instance) profile: ProfileModel = ProfileModel.objects.create(pk = instance.pk, user = instance)
profile.save() profile.save()
class BlockModel(models.Model):
blocker = IntegerField(primary_key=False)
blocked = IntegerField(primary_key=False)
def __str__(self):
return "blocker_id: " + str(self.blocker) + ", blocked_id: " + str(self.blocked)

View File

@ -7,8 +7,8 @@ class ProfileTest(TestCase):
def setUp(self): def setUp(self):
self.user: User = User.objects.create(username='bozo', password='password') self.user: User = User.objects.create(username='bozo', password='password')
self.user.save() self.user.save()
self.expected_response = {'avatar_url': '/static/avatars/default.avif', 'user_id': 1, 'username': 'bozo'} self.expected_response = {"name": "bozo",
"title": ""}
self.url = "/api/profiles/" self.url = "/api/profiles/"
def test_profile_create_on_user_created(self): def test_profile_create_on_user_created(self):

View File

@ -3,13 +3,9 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from . import viewsets from . import viewsets
from . import views
urlpatterns = [ urlpatterns = [
path("me", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'get': 'retrieve'}), name="my_profile_page"), path("<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve', 'patch': 'partial_update'}), name="profile_page"),
path("<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"), path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
path("block", views.BlocksView.as_view(), name="block_page"), #path("me", viewsets.ProfileViewSet.as_view(), name="my_profile_page"),
path("block/<int:pk>", views.BlockView.as_view(), name="block_page"), ] + static("/static/avatars/", document_root="./avatars")
] + static("/static/avatars/", document_root="./avatars")

View File

@ -1,66 +0,0 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions, status
from rest_framework.authentication import SessionAuthentication
from rest_framework.renderers import JSONRenderer
from django.core import serializers
from .models import BlockModel
class BlockView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request, pk):
block = BlockModel.objects.filter(pk=pk)
if (block.exists()):
return Response(serializers.serialize("json", block), status=status.HTTP_200_OK)
else:
return Response("Not Found", status=status.HTTP_404_NOT_FOUND)
class BlocksView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request):
blocks = BlockModel.objects.filter(blocker=request.user.pk)
if (blocks):
return Response(serializers.serialize("json", BlockModel.objects.filter(blocker=request.user.pk)), status=status.HTTP_200_OK)
else:
return Response({}, status=status.HTTP_404_NOT_FOUND)
def post(self, request):
data: dict = request.data
users_id = request.data.get("users_id", None)
if (users_id == None):
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
if (BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])):
return Response({"Already Exist"}, status=status.HTTP_409_CONFLICT)
new_block = BlockModel()
new_block.blocker = users_id[0]
new_block.blocked = users_id[1]
new_block.save()
return Response({"block_id": new_block.pk}, status=status.HTTP_201_CREATED)
def delete(self, request):
data: dict = request.data
users_id = request.data.get("users_id", None)
if (users_id == None):
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
block = BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])
print(list(block))
if (block.count() > 1):
return Response("Not normal >:[", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if (not block):
return Response("Don't exist", status=status.HTTP_404_NOT_FOUND)
block.delete()
return Response("Deleted", status=status.HTTP_200_OK)

View File

@ -3,7 +3,6 @@ from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import permissions, status from rest_framework import permissions, status
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from django.http import HttpRequest from django.http import HttpRequest
from django.db.models import QuerySet from django.db.models import QuerySet
@ -18,9 +17,7 @@ class ProfileViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def retrieve(self, request: HttpRequest, pk=None): def retrieve(self, request: HttpRequest, pk=None):
if (not self.queryset().filter(pk=pk).exists()): instance = ProfileModel.objects.get(pk=pk)
return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND)
instance = self.queryset().get(pk=pk)
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:] instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
return Response(self.serializer_class(instance).data, return Response(self.serializer_class(instance).data,
status=status.HTTP_200_OK) status=status.HTTP_200_OK)
@ -31,28 +28,17 @@ class ProfileViewSet(viewsets.ModelViewSet):
profile["avatar_url"] = profile["avatar_url"][profile["avatar_url"].find("static") - 1:] profile["avatar_url"] = profile["avatar_url"][profile["avatar_url"].find("static") - 1:]
return Response(serializer.data) return Response(serializer.data)
class MyProfileViewSet(viewsets.ModelViewSet): def perform_create(self, serializer):
serializer.save(user=self.request.user)
permission_classes = (permissions.IsAuthenticated,) def perform_update(self, serializer):
authentication_classes = (SessionAuthentication,) query: QuerySet = ProfileModel.objects.filter(pk=self.request.user.pk)
serializer_class = ProfileSerializer if (not query.exists()):
queryset = ProfileModel.objects.all return Response("profile not found", status=status.HTTP_400_BAD_REQUEST)
profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk)
def get_object(self):
obj = self.queryset().get(pk=self.request.user.pk)
return obj
def perform_update(self, serializer, pk=None):
profile: ProfileModel = self.get_object()
avatar = self.request.data.get("file", None) avatar = self.request.data.get("file", None)
if (avatar is not None): if (avatar is not None):
if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"): if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"):
profile.avatar_url.storage.delete(profile.avatar_url.name) profile.avatar_url.storage.delete(profile.avatar_url.name)
profile.avatar_url = avatar profile.avatar_url = avatar
profile.save() profile.save()
def retrieve(self, request: HttpRequest, pk=None):
instance: ProfileModel = self.get_object()
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
return Response(self.serializer_class(instance).data,
status=status.HTTP_200_OK)

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@ -1,6 +0,0 @@
from django.apps import AppConfig
class TournamentConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tournament'

View File

@ -1,43 +0,0 @@
from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
from games.models import GameModel
import json
from .models import tournament_manager, TournamentMember, TournamentRoom, TournamentRoomManager
class TournamentWebConsumer(WebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.channel_name = "tournament"
self.group_name = "tournament"
def connect(self):
self.user: User = self.scope["user"]
if (self.user.is_anonymous or not self.user.is_authenticated):
return
self.channel_layer.group_add(self.group_name, self.channel_name)
self.tournament_id = int(self.scope['url_route']['kwargs']['tournament_id'])
self.room = tournament_manager.get(self.tournament_id)
self.member = TournamentMember(self.user.pk, self, self.room)
if (self.room is None):
self.member.send("Tournament not found")
self.disconnect(1017)
self.room.append(self.member)
def receive(self, text_data: str = None, bytes_data: bytes = None):
self.member.receive(text_data, bytes_data)
def disconnect(self, close_code):
member = self.room.get_member_by_socket(self)
if (member is not None):
self.room.remove(self.member, close_code)

View File

@ -1,154 +0,0 @@
from django.db import models
from channels.generic.websocket import WebsocketConsumer
from games.models import GameModel
import json
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from transcendence.abstract.AbstractRoom import AbstractRoom
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
# Create your models here.tu
class TournamentModel(models.Model):
name = models.CharField(max_length = 100)
nb_players = models.IntegerField()
nb_players_by_game = models.IntegerField()
level = models.IntegerField()
started = models.BooleanField(default = False)
finished = models.BooleanField(default = False)
def create_game(self, level, players_id):
game_id = GameModel().create(players_id = players_id)
TournamentGamesModel(game_id = game_id, tournament_id = self.pk, tournament_level = level).save()
return game_id
def get_games_id_by_level(self, level):
tmp = TournamentGamesModel.objects.filter(tournament_id = self.pk, tournament_level = level)
return [instance.game_id for instance in tmp]
def get_games_id(self):
return [tournament_game.game_id for tournament_game in TournamentGamesModel.objects.filter(tournament_id = self.pk)]
def get_players_id(self):
return [model.participant_id for model in TournamentParticipantsModel.objects.filter(tournament_id=self.pk)]
def is_a_participant(self, participant_id: int):
return TournamentParticipantsModel.objects.filter(participant_id = participant_id, tournament_id = self.pk).exists()
def add_participants(self, participants_id: [int]):
for participant_id in participants_id:
TournamentParticipantsModel(tournament_id = self.pk, participant_id = participant_id).save()
def start(self, participants_id: [int]):
self.started = True
self.add_participants(participants_id)
games_id = [int]
for i in range(0, len(participants_id), self.nb_players_by_game):
game_id = self.create_game(0, participants_id[i : i + self.nb_players_by_game])
games_id.append(game_id)
self.save()
return games_id
class TournamentParticipantsModel(models.Model):
tournament_id = models.IntegerField()
participant_id = models.IntegerField()
class TournamentGamesModel(models.Model):
tournament_id = models.IntegerField()
tournament_level = models.IntegerField()
game_id = models.IntegerField()
class TournamentMember(AbstractRoomMember):
def __init__(self, user_id: int, socket: WebsocketConsumer, room):
super().__init__(user_id, socket)
self.participate = False
self.room = room
def receive(self, text_data: str = None, byte_dates: bytes = None):
if (text_data is None):
return
data: dict = json.loads(text_data)
if (data.get("participate") is not None):
self.room.update_participants(self)
def send_error_message(self, message: str):
self.send("error", {"error_message": message})
def go_to(self, url: str):
self.send("go_to", {"url": url})
def send_participating(self):
self.send("is_participant", {"is_participant": self.participate})
class TournamentRoom(AbstractRoom):
def __init__(self, room_manager, tournament_id: int):
super().__init__(room_manager)
self.tournament_id = tournament_id
self.tournament = TournamentModel.objects.get(pk = tournament_id)
def start(self):
self.broadcast("tournament_start")
games_id = self.tournament.start(self.get_participants_id())
for i, participant in enumerate(self.get_participants()):
participant: TournamentMember
participant.go_to(f"games/{games_id[i // self.tournament.nb_players_by_game]}")
def update_participants(self, member: TournamentMember):
if (self.tournament.started):
member.send_error_message("Tournament already started")
return
member.participate = not member.participate
nb_participants = self.get_nb_participants()
self.broadcast("update_participants", {"nb_participants": nb_participants})
member.send_participating()
if (nb_participants == self.tournament.nb_players):
self.start()
def get_nb_participants(self):
if (self.tournament.started):
return self.tournament.nb_players
nb_participants = 0
for member in self._member_list:
member: TournamentMember
if (member.participate):
nb_participants += 1
return nb_participants
def get_participants(self):
return [member for member in self._member_list if member.participate]
def get_participants_id(self):
return [member.user_id for member in self._member_list if member.participate]
def append(self, member: TournamentMember):
super().append(member)
if self.tournament.started:
member.participate = self.tournament.is_a_participant(member.user_id)
member.send_participating()
member.send("nb_participants", {"nb_participants": self.get_nb_participants()})
class TournamentRoomManager(AbstractRoomManager):
def get(self, tournament_id: int):
for room in self._room_list:
if (room.tournament_id == tournament_id):
return room
if (TournamentModel.objects.filter(pk = tournament_id).exists()):
room = TournamentRoom(self, tournament_id)
self.append(room)
return room
return None
tournament_manager: TournamentRoomManager = TournamentRoomManager()

View File

@ -1,6 +0,0 @@
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/games/(?P<game_id>\d+)$', consumers.TournamentWebConsumer.as_asgi())
]

View File

@ -1,38 +0,0 @@
from rest_framework import serializers
from .models import TournamentModel
from games.serializers import GameSerializer
class TournamentSerializer(serializers.ModelSerializer):
levels = serializers.SerializerMethodField(read_only=True, required=False)
level = serializers.ReadOnlyField()
started = serializers.ReadOnlyField()
finished = serializers.ReadOnlyField()
name = serializers.CharField(default="")
class Meta:
model = TournamentModel
fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id"]
def get_levels(self, instance: TournamentModel):
levels: [[int]] = []
for i in range(instance.level):
games_id: [int] = instance.get_games_id_by_level(i)
if (games_id == []):
break
levels.append(games_id)
return levels
def validate_nb_players(self, value: int):
if (value < 2):
raise serializers.ValidationError("The numbers of players must be greather than 2.")
return value
def validate_nb_players_by_game(self, value: int):
if (value < 2):
raise serializers.ValidationError("The numbers of players by game must be greather than 2.")
nb_players: str = self.initial_data.get("nb_players")
if (nb_players is not None and nb_players.isnumeric()):
nb_players: int = int(nb_players)
if (value > nb_players):
raise serializers.ValidationError("The numbers of players by game must be smaller than the numbers of players.")
return value

View File

@ -1,44 +0,0 @@
from django.test import TestCase
# Create your tests here.
from django.test.client import Client
from django.http import HttpResponse
from django.contrib.auth.models import User
import json
import uuid
class CreateTest(TestCase):
def setUp(self):
self.client = Client()
self.url = "/api/tournaments/"
self.username = str(uuid.uuid4())
self.password = str(uuid.uuid4())
self.nb_players_by_game = 2
self.nb_players = 8
user: User = User.objects.create_user(username=self.username, password=self.password)
self.client.login(username=self.username, password=self.password)
def test_normal(self):
response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": self.nb_players_by_game, "nb_players": self.nb_players})
response_data: dict = json.loads(response.content)
self.assertDictContainsSubset({"name": ""}, response_data)
def test_too_small_nb_players_by_game(self):
response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": 1, "nb_players": self.nb_players})
response_data = json.loads(response.content)
self.assertDictEqual(response_data, {'nb_players_by_game': ['The numbers of players by game must be greather than 2.']})
def test_too_small_nb_players(self):
response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": self.nb_players_by_game, "nb_players": 1})
response_data = json.loads(response.content)
self.assertDictEqual(response_data, {'nb_players': ['The numbers of players must be greather than 2.'], 'nb_players_by_game': ['The numbers of players by game must be smaller than the numbers of players.']})
def test_nb_players_smaller_nb_players_by_game(self):
response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": 5, "nb_players": 3})
response_data = json.loads(response.content)
self.assertDictEqual(response_data, {'nb_players_by_game': ['The numbers of players by game must be smaller than the numbers of players.']})

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,11 +0,0 @@
from django.urls import path, re_path
from django.conf import settings
from django.conf.urls.static import static
from .viewset import TournamentViewSet
urlpatterns = [
path("<int:pk>", TournamentViewSet.as_view({"get": "retrieve"}), name="tournament_page"),
path("", TournamentViewSet.as_view({"post": "create"}), name="tournament_page"),
re_path(r"search/(?P<state>\w*)", TournamentViewSet.as_view({"get": "list", }), name="tournaments"),
]

View File

@ -1,56 +0,0 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import permissions, status
from rest_framework.authentication import SessionAuthentication
from django.http import HttpRequest
from django.contrib.auth import login
from django.db.models import QuerySet
from .models import TournamentModel
from .serializers import TournamentSerializer
# Create your views here.
class TournamentViewSet(viewsets.ModelViewSet):
queryset = TournamentModel.objects.all
serializer_class = TournamentSerializer
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def perform_create(self, serializer: TournamentSerializer):
nb_players = serializer.validated_data["nb_players"]
nb_players_by_game = serializer.validated_data["nb_players_by_game"]
level = 1
number: int = nb_players
while (number != nb_players_by_game):
number = number // 2 + (number % 2)
level += 1
tournament = serializer.save(level = level)
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
def list(self, request: HttpRequest, state: str = ""):
query: QuerySet
match state:
case "started":
query = TournamentModel.objects.filter(started=True, finished=False)
case "finished":
query = TournamentModel.objects.filter(finished=True)
case "waiting":
query = TournamentModel.objects.filter(started=False, finished=False)
case _:
query = TournamentModel.objects.all()
serializer = TournamentSerializer(query, many=True)
return Response(serializer.data)
def retrieve(self, request: HttpRequest, pk):
if (not TournamentModel.objects.filter(pk=pk).exists()):
return Response({"detail": "Tournament not found."}, status=status.HTTP_404_NOT_FOUND)
tournament = TournamentModel.objects.get(pk=pk)
return Response(self.serializer_class(tournament).data, status=status.HTTP_200_OK)

View File

@ -13,8 +13,6 @@ from channels.auth import AuthMiddlewareStack
import chat.routing import chat.routing
import matchmaking.routing import matchmaking.routing
import tournament.routing
import games.routing
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
@ -25,9 +23,7 @@ application = ProtocolTypeRouter({
'websocket':AuthMiddlewareStack( 'websocket':AuthMiddlewareStack(
URLRouter( URLRouter(
chat.routing.websocket_urlpatterns + chat.routing.websocket_urlpatterns +
matchmaking.routing.websocket_urlpatterns + matchmaking.routing.websocket_urlpatterns
tournament.routing.websocket_urlpatterns +
games.routing.websocket_urlpatterns
) )
) )
}) })

View File

@ -1,5 +1,5 @@
""" """
Django settings for transcendence project. Django settings for trancendence project.
Generated by 'django-admin startproject' using Django 4.2.6. Generated by 'django-admin startproject' using Django 4.2.6.
@ -43,7 +43,6 @@ INSTALLED_APPS = [
'channels', 'channels',
'daphne', 'daphne',
'tournament.apps.TournamentConfig',
'matchmaking.apps.MatchmakingConfig', 'matchmaking.apps.MatchmakingConfig',
'games.apps.GamesConfig', 'games.apps.GamesConfig',
'accounts.apps.AccountsConfig', 'accounts.apps.AccountsConfig',
@ -61,7 +60,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
] ]
ASGI_APPLICATION = 'transcendence.asgi.application' ASGI_APPLICATION = 'trancendence.asgi.application'
CHANNEL_LAYERS = { CHANNEL_LAYERS = {
'default' :{ 'default' :{
@ -81,7 +80,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'transcendence.urls' ROOT_URLCONF = 'trancendence.urls'
TEMPLATES = [ TEMPLATES = [
{ {
@ -99,7 +98,7 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'transcendence.wsgi.application' WSGI_APPLICATION = 'trancendence.wsgi.application'
# Database # Database

View File

@ -22,7 +22,5 @@ urlpatterns = [
path('api/profiles/', include('profiles.urls')), path('api/profiles/', include('profiles.urls')),
path('api/accounts/', include('accounts.urls')), path('api/accounts/', include('accounts.urls')),
path('api/chat/', include('chat.urls')), path('api/chat/', include('chat.urls')),
path('api/tournaments/', include('tournament.urls')),
path('api/games/', include('games.urls')),
path('', include('frontend.urls')), path('', include('frontend.urls')),
] ]

View File

@ -1,51 +0,0 @@
from channels.generic.websocket import WebsocketConsumer
from .AbstractRoomMember import AbstractRoomMember
class AbstractRoom:
def __init__(self, room_manager):
self._member_list: [AbstractRoomMember] = []
self.room_manager = room_manager
def broadcast(self, detail: str, data: dict = {}):
for member in self._member_list:
member: AbstractRoomMember
member.send(detail, data)
def clear(self):
self._member_list.clear()
def get_member_by_socket(self, socket: WebsocketConsumer):
for member in self._member_list:
member: AbstractRoomMember
if (member.socket is socket):
return member
return None
def get_member_by_user_id(self, user_id: int):
for member in self._member_list:
member: AbstractRoomMember
if (member.user_id == user_id):
return member
return None
def append(self, member: AbstractRoomMember):
self._member_list.append(member)
member.accept()
def remove(self, member: AbstractRoomMember, code: int = 1000):
self._member_list.remove(member)
member.disconnect(code)
def empty(self):
for _ in self._member_list:
return False
return True
def get_users_id(self):
return [member.user_id for member in self._member_list]
def __len__(self):
return len(self._member_list)

View File

@ -1,12 +0,0 @@
from .AbstractRoom import AbstractRoom
class AbstractRoomManager:
def __init__(self):
self._room_list: [AbstractRoom] = []
def remove(self, room: AbstractRoom):
self._room_list.remove(room)
def append(self, room: AbstractRoom):
self._room_list.append(room)

View File

@ -1,20 +0,0 @@
from channels.generic.websocket import WebsocketConsumer
import json
class AbstractRoomMember:
def __init__(self, user_id: int, socket: WebsocketConsumer):
self.user_id: int = user_id
self.socket: WebsocketConsumer = socket
def send(self, detail: str, data: dict = {}):
raw_data: dict = {"detail": detail}
raw_data.update(data)
self.socket.send(text_data=json.dumps(raw_data))
def accept(self):
self.socket.accept()
def disconnect(self, code: int = 1000):
self.socket.disconnect(code)