diff --git a/backend/api/database/auth/crud.py b/backend/api/database/auth/crud.py index d8ab4de..02d7f04 100644 --- a/backend/api/database/auth/crud.py +++ b/backend/api/database/auth/crud.py @@ -2,7 +2,8 @@ import uuid from services.password import get_password_hash from database.auth.models import User, UserEdit from sqlmodel import Session, select - +from jose import jwt, exceptions +from config import SECRET_KEY, ALGORITHM def create_user_db(username:str , password: str, db: Session): user = User(username=username, hashed_password=password, clientId=uuid.uuid4()) db.add(user) @@ -18,6 +19,17 @@ def get_user_from_clientId_db(clientId: str , db: Session): user = db.exec(select(User).where(User.clientId == clientId)).first() return user + +def get_user_from_token(token: str, db: Session): + try: + decoded = jwt.decode(token=token, key=SECRET_KEY, + algorithms=[ALGORITHM]) + except exceptions.ExpiredSignatureError: + return False + + clientId = decoded.get('sub') + return get_user_from_clientId_db(clientId=clientId, db=db) + def update_user_db(clientId: str, user: UserEdit, db: Session): db_user = get_user_from_clientId_db(clientId, db) diff --git a/backend/api/database/room/crud.py b/backend/api/database/room/crud.py index 2a4ae64..8b5f68a 100644 --- a/backend/api/database/room/crud.py +++ b/backend/api/database/room/crud.py @@ -1,22 +1,30 @@ from fastapi import Depends -from sqlmodel import Session, select +from sqlmodel import Session, select, col from database.db import get_session from database.room.models import Anonymous, Member, Room, RoomCreate from database.auth.models import User from services.database import generate_unique_code +from database.auth.crud import get_user_from_token + + +def check_room(room_id: str, db: Session = Depends(get_session)): + room = db.exec(select(Room).where(Room.id_code == room_id)).first() + return room + def create_room_db(*,room: RoomCreate, user: User | None = None, username: str | None = None, db: Session): id_code = generate_unique_code(Room,s=db) + member_id = generate_unique_code(Member, s=db) room_obj = Room(**room.dict(exclude_unset=True), id_code=id_code) if user is not None: - member = Member(user_id=user.id, room=room_obj, is_admin=True) + member = Member(user_id=user.id, room=room_obj, is_admin=True, id_code=member_id) db.add(member) db.commit() db.refresh(member) if username is not None: reconnect_code = generate_unique_code(Anonymous, s=db, field_name='reconnect_code') anonymous = Anonymous(username=username, reconnect_code=reconnect_code) - member = Member(anonymous=anonymous, room=room_obj, is_admin=True) + member = Member(anonymous=anonymous, room=room_obj, is_admin=True, id_code=member_id) db.add(member) db.commit() db.refresh(member) @@ -25,10 +33,159 @@ def create_room_db(*,room: RoomCreate, user: User | None = None, username: str | return {"room": room_obj, "member": member} -def check_room(room_id: str, db: Session = Depends(get_session)): - room = db.exec(select(Room).where(Room.id_code==room_id)).first() - return room -def userInRoom(room: Room, user: User, db: Session): - member = db.exec(select(Member).where(Member.room_id == room.id, Member.user_id == user.id)).first() - return member +def get_member_from_user(user_id: int, room_id: int, db: Session): + member = db.exec(select(Member).where(Member.room_id == + room_id, Member.user_id == user_id)).first() + return member + + +def get_member_from_token(token: str, room_id: int, db: Session): + user = get_user_from_token(token, db) + if user is False: + return False + if user is None: + return None + member = get_member_from_user(user.id, room_id, db) + return member + + +def get_member_from_anonymous(anonymous_id: int, room_id: int, db: Session): + member = db.exec(select(Member).where(Member.room_id == + room_id, Member.anonymous_id == anonymous_id)).first() + return member + + +def get_member_from_reconnect_code(reconnect_code: str, room_id: int, db: Session): + anonymous = get_anonymous_from_code(reconnect_code, db) + if anonymous is None: + return None + member = get_member_from_anonymous(anonymous.id, room_id, db) + return member + + +def get_anonymous_from_code(reconnect_code: str, db: Session): + anonymous = db.exec(select(Anonymous).where( + Anonymous.reconnect_code == reconnect_code)).first() + return anonymous + + +def connect_member(member: Member, db: Session): + member.online = True + db.add(member) + db.commit() + db.refresh(member) + return member + + +def disconnect_member(member: Member, db: Session): + if member.waiting == False: + member.online = False + db.add(member) + db.commit() + db.refresh(member) + return member + else: + db.delete(member) + db.commit() + return member + + +def validate_username(username: str, room: Room, db: Session = Depends(get_session)): + print('VALIDATE', username) + if len(username) > 20: + return None + members = select(Member.anonymous_id).where( + Member.room_id == room.id, Member.anonymous_id != None) + anonymous = select(Anonymous).where( + col(Anonymous.id).in_(members), Anonymous.username == username) + username_anonymous = db.exec(anonymous).first() + return None if username_anonymous is not None else username + + +def create_anonymous_member(username: str, room: Room, db: Session): + username = validate_username(username, room, db) + if username is None: + return None + reconnect_code = generate_unique_code( + Anonymous, s=db, field_name="reconnect_code") + anonymous = Anonymous(username=username, reconnect_code=reconnect_code) + member_id = generate_unique_code(Member, s=db) + member = Member(room=room, anonymous=anonymous, id_code=member_id) + db.add(member) + db.commit() + db.refresh(member) + return member + + +def create_user_member(user: User, room: Room, db: Session): + member = get_member_from_user(user.id, room.id, db) + if member is not None: + return None + member_id = generate_unique_code(Member, s=db) + member = Member(room=room, user=user, id_code=member_id) + db.add(member) + db.commit() + db.refresh(member) + return member + + +def create_anonymous_waiter(username: str, room: Room, db: Session): + username = validate_username(username, room, db) + if username is None: + return None + reconnect_code = generate_unique_code( + Anonymous, s=db, field_name="reconnect_code") + anonymous = Anonymous(username=username, reconnect_code=reconnect_code) + + member_id = generate_unique_code(Member, s=db) + member = Member(room=room, anonymous=anonymous, + waiting=True, id_code=member_id) + db.add(member) + db.commit() + db.refresh(member) + return member + + +def create_user_waiter(user: User, room: Room, db: Session): + member = get_member_from_user(user.id, room.id, db) + if member is not None: + return member + member_id = generate_unique_code(Member, s=db) + member = Member(room=room, user=user, waiting=True, + id_code=member_id) + db.add(member) + db.commit() + db.refresh(member) + return member + + +def get_waiter(waiter_code: str, db: Session): + return db.exec(select(Member).where(Member.id_code == waiter_code)).first() + + +def get_member(id_code: str,room_id:str, db: Session): + return db.exec(select(Member).where(Member.id_code == id_code, Member.room_id== room_id)).first() +def delete_member(member: Member, db: Session): + db.delete(member) + db.commit() + return None + +def accept_waiter(member: Member, db: Session): + member.waiting = False + member.waiter_code = None + db.add(member) + db.commit() + db.refresh(member) + return member + + +def refuse_waiter(member: Member, db: Session): + db.delete(member) + db.commit() + return None + +def leave_room(member: Member, db: Session): + db.delete(member) + db.commit() + return None diff --git a/backend/api/database/room/models.py b/backend/api/database/room/models.py index 8c7071e..71e2965 100644 --- a/backend/api/database/room/models.py +++ b/backend/api/database/room/models.py @@ -54,6 +54,7 @@ class Member(SQLModel, table = True): online: bool = False waiter_code: Optional[str] = Field(default= None) + id_code: str class RoomRead(RoomBase): id_code: str #members: List['Member'] @@ -74,7 +75,7 @@ class MemberRead(SQLModel): reconnect_code: str = '' isUser: bool isAdmin: bool - + id_code: str class MemberSerializer(MemberRead): member: MemberWithRelations diff --git a/backend/api/routes/room/routes.py b/backend/api/routes/room/routes.py index 1429af7..79c6f75 100644 --- a/backend/api/routes/room/routes.py +++ b/backend/api/routes/room/routes.py @@ -1,9 +1,6 @@ -from pydantic import Field -from pydantic import create_model_from_typeddict +from database.room.crud import create_room_db, delete_member, get_member, get_member_from_token, get_member_from_reconnect_code, connect_member, disconnect_member, create_anonymous_member, create_anonymous_waiter, create_user_member, create_user_waiter, get_waiter, accept_waiter, leave_room, refuse_waiter, check_room from pydantic.error_wrappers import ValidationError from pydantic import validate_arguments -from services.database import generate_unique_code -from sqlmodel import col from sqlmodel import select from pydantic import BaseModel from typing import Any, Callable, Dict, List, Optional @@ -12,7 +9,7 @@ from config import ALGORITHM, SECRET_KEY from database.auth.crud import get_user_from_clientId_db from database.auth.models import User from database.db import get_session -from database.room.crud import check_room, create_room_db, userInRoom + from sqlmodel import Session from database.room.models import Anonymous, MemberSerializer, MemberWithRelations, MemberRead, Room, RoomCreate, RoomRead, Member from services.auth import get_current_user_optional @@ -35,9 +32,9 @@ class Waiter(BaseModel): def serialize_member(member: Member) -> MemberRead | Waiter: member_obj = member.user or member.anonymous if member.waiting == False: - return MemberRead(username=member_obj.username, reconnect_code=getattr(member_obj, "reconnect_code", ""), isUser=member.user_id != None, isAdmin=member.is_admin) + return MemberRead(username=member_obj.username, reconnect_code=getattr(member_obj, "reconnect_code", ""), isUser=member.user_id != None, isAdmin=member.is_admin, id_code=member.id_code) if member.waiting == True: - return Waiter(username=member_obj.username, waiter_id=member.waiter_code) + return Waiter(username=member_obj.username, waiter_id=member.id_code) @router.post('/room', response_model=RoomAndMember) @@ -70,11 +67,16 @@ class ConnectionManager: def make_event_decorator(eventsDict): - def _(name: str, conditions: List[Callable | bool] = []): + def _(name: str | List, conditions: List[Callable | bool] = []): def add_event(func): model = validate_arguments(func).model - eventsDict[name] = {"func": func, - "conditions": conditions, "model": model} + if type(name) == str: + eventsDict[name] = {"func": func, + "conditions": conditions, "model": model} + if type(name) == list: + for n in name: + eventsDict[n] = {"func": func, + "conditions": conditions, "model": model} return func return add_event return _ @@ -131,7 +133,7 @@ class Consumer: async def send(self, payload): type = payload.get('type', None) - print('TYPE', type, self.member.id) + print('TYPE', type, self.member) if type is not None: event_wrapper = self.sendings.get(type, None) if event_wrapper is not None: @@ -147,6 +149,7 @@ class Consumer: try: validated_payload = model(self=self, **data) except ValidationError as e: + print("ERROR", e) await self.ws.send_json({"type": "error", "data": {"msg": "Oops there was an error"}}) return @@ -154,19 +157,16 @@ class Consumer: exclude=["v__duplicate_kwargs", "args", 'kwargs', "self"]) try: parsed_payload = handler( - self, **validated_payload) - - + self, **validated_payload) + await self.ws.send_json({'type': type, "data": dict_all(parsed_payload)}) return except Exception as e: - - print('NOPE', self.member.id, e) return return - print('pls') + #print('pls') await self.ws.send_json(payload) - print('sent') + #print('sent') async def receive(self, data): event = data.get('type', None) @@ -211,9 +211,9 @@ class ConsumerManager: if group not in self.active_connections: self.active_connections[group] = [] - print("adding", ws, self.active_connections[group]) + #print("adding", ws, self.active_connections[group]) if ws not in self.active_connections[group]: - print('ACTUALLY ADDING') + #print('ACTUALLY ADDING') self.active_connections[group].append(ws) def remove(self, group: str, ws: Consumer): @@ -223,14 +223,13 @@ class ConsumerManager: async def broadcast(self, message, group: str, exclude: list[Consumer] = []): if group in self.active_connections: - print(self.active_connections[group], exclude) + #print(self.active_connections[group], exclude) for connection in list(set(self.active_connections[group])): if connection not in exclude: - print('SEND TO', connection, message) + #print('SEND TO', connection, message) await connection.send(message) - -manager = ConsumerManager() + class Token(BaseModel): @@ -248,151 +247,12 @@ def get_user_from_token(token: str, db: Session): return get_user_from_clientId_db(clientId=clientId, db=db) -def get_member_from_user(user_id: int, room_id: int, db: Session): - member = db.exec(select(Member).where(Member.room_id == - room_id, Member.user_id == user_id)).first() - return member - - -def get_member_from_token(token: str, room_id: int, db: Session): - user = get_user_from_token(token, db) - if user is False: - return False - if user is None: - return None - member = get_member_from_user(user.id, room_id, db) - return member - - -def get_member_from_anonymous(anonymous_id: int, room_id: int, db: Session): - member = db.exec(select(Member).where(Member.room_id == - room_id, Member.anonymous_id == anonymous_id)).first() - return member - - -def get_member_reconnect_code(reconnect_code: str, room_id: int, db: Session): - anonymous = get_anonymous_from_code(reconnect_code, db) - if anonymous is None: - return None - member = get_member_from_anonymous(anonymous.id, room_id, db) - return member - - -def get_anonymous_from_code(reconnect_code: str, db: Session): - anonymous = db.exec(select(Anonymous).where( - Anonymous.reconnect_code == reconnect_code)).first() - return anonymous - - -def connect_member(member: Member, db: Session): - member.online = True - db.add(member) - db.commit() - db.refresh(member) - return member - - -def disconnect_member(member: Member, db: Session): - if member.waiting == False: - member.online = False - db.add(member) - db.commit() - db.refresh(member) - return member - else: - db.delete(member) - db.commit() - return member - - -def validate_username(username: str, room: Room, db: Session = Depends(get_session)): - print('VALIDATE', username) - if len(username) > 20: - return None - members = select(Member.anonymous_id).where( - Member.room_id == room.id, Member.anonymous_id != None) - anonymous = select(Anonymous).where( - col(Anonymous.id).in_(members), Anonymous.username == username) - username_anonymous = db.exec(anonymous).first() - return None if username_anonymous is not None else username - - -def create_anonymous_member(username: str, room: Room, db: Session): - username = validate_username(username) - if username is None: - return None - reconnect_code = generate_unique_code( - Anonymous, s=db, field_name="reconnect_code") - anonymous = Anonymous(username=username, reconnect_code=reconnect_code) - member = Member(room=room, anonymous=anonymous) - db.add(member) - db.commit() - db.refresh(member) - return member - - -def create_user_member(user: User, room: Room, db: Session): - member = get_member_from_user(user.id, room.id, db) - if member is not None: - return None - member = Member(room=room, user=user) - db.add(member) - db.commit() - db.refresh(member) - return member - - -def create_anonymous_waiter(username: str, room: Room, db: Session): - username = validate_username(username, room, db) - if username is None: - return None - reconnect_code = generate_unique_code( - Anonymous, s=db, field_name="reconnect_code") - anonymous = Anonymous(username=username, reconnect_code=reconnect_code) - - waiter_code = generate_unique_code(Member, s=db, field_name="waiter_code") - member = Member(room=room, anonymous=anonymous, - waiting=True, waiter_code=waiter_code) - db.add(member) - db.commit() - db.refresh(member) - return member - - -def create_user_waiter(user: User, room: Room, db: Session): - member = get_member_from_user(user.id, room.id, db) - if member is not None: - return None - waiter_code = generate_unique_code(Member, s=db, field_name="waiter_code") - member = Member(room=room, user=user, waiting=True, - waiter_code=waiter_code) - db.add(member) - db.commit() - db.refresh(member) - return member - - -def get_waiter(waiter_code: str, db: Session): - return db.exec(select(Member).where(Member.waiter_code == waiter_code)).first() - - -def accept_waiter(member: Member, db: Session): - member.waiting = False - member.waiter_code = None - db.add(member) - db.commit() - db.refresh(member) - return member - -def refuse_waiter(member: Member, db: Session): - db.delete(member) - db.commit() - return None - +def check_same_member(member: Member, memberR: MemberRead): + return (member.user is not None and memberR.username == member.user.username) or (member.anonymous and memberR.reconnect_code == member.anonymous.reconnect_code) class RoomConsumer(Consumer): - def __init__(self, ws: WebSocket, room: Room, manager: ConsumerManager, db: Session): + def __init__(self, ws: WebSocket, room: Room, manager: "RoomManager", db: Session): self.room = room self.ws = ws self.manager = manager @@ -406,39 +266,30 @@ class RoomConsumer(Consumer): async def direct_send(self, type: str, payload: Any): await self.ws.send_json({'type': type, "data": payload}) - async def send_to_all_room(self, type: str, payload: Any, exclude: bool = False): - await self.manager.broadcast({'type': type, "data": payload}, f'{self.room.id}__member', [exclude == True and self.ws]) - await self.manager.broadcast({'type': type, "data": payload}, f'{self.room.id}__admin', [exclude == True and self.ws]) - async def send_to_admin(self, type: str, payload: Any, exclude: bool = False): - await self.manager.broadcast({'type': type, "data": payload}, f'{self.room.id}__admin', [exclude == True and self.ws]) + await self.manager.send_to_admin(self.room.id, {'type': type, "data": payload}) + + async def send_to(self, type: str, payload: Any,member_id, exclude: bool = False): + await self.manager.send_to(self.room.id, member_id,{'type': type, "data": payload}) - async def send_to_members(self, type: str, payload: Any, exclude: bool = False): - await self.manager.broadcast({'type': type, "data": payload}, f'{self.room.id}__member', [exclude == True and self.ws]) - - async def broadcast(self, type, payload, exclude= False): - await self.manager.broadcast({"type": type, "data": payload}, self.room.id, exclude = [exclude == True and self]) - - def add_to_admin(self): - self.manager.add(f'{self.room.id}__admin', self.ws) - - def add_to_members(self): - self.manager.add(f'{self.room.id}__members', self.ws) + async def broadcast(self, type, payload, exclude=False): + await self.manager.broadcast({"type": type, "data": payload}, self.room.id, exclude=[exclude == True and self]) def add_to_group(self): - if self.member.waiting == True: - self.manager.add(f'waiter__{self.member.waiter_code}', self) self.manager.add(self.room.id, self) async def connect_self(self): if isinstance(self.member, Member): connect_member(self.member, self.db) - await self.broadcast(type="connect", payload={"member": serialize_member(self.member).dict()}) + await self.broadcast(type="connect", payload={"member": serialize_member(self.member).dict()}, exclude=True) async def disconnect_self(self): if isinstance(self.member, Member): disconnect_member(self.member, self.db) - await self.broadcast(type="disconnect", payload={"member": serialize_member(self.member).dict()}) + if self.member.waiting is False: + await self.broadcast(type="disconnect", payload={"member": serialize_member(self.member).dict()}) + else: + await self.broadcast(type="disconnect_waiter", payload={"waiter": serialize_member(self.member).dict()}) # DB Utilities @@ -455,12 +306,12 @@ class RoomConsumer(Consumer): return self.member = member - # await self.connect_self() + await self.connect_self() self.add_to_group() await self.direct_send(type="loggedIn", payload={"member": serialize_member(self.member).dict()}) elif reconnect_code is not None: - member = get_member_reconnect_code( + member = get_member_from_reconnect_code( reconnect_code, self.room.id, db=self.db) if member is None: await self.direct_send(type="error", payload={"msg": "Utilisateur introuvable dans cette salle"}) @@ -468,7 +319,7 @@ class RoomConsumer(Consumer): self.member = member - # await self.connect_self() + await self.connect_self() self.add_to_group() await self.direct_send(type="loggedIn", payload={"member": serialize_member(self.member).dict()}) if reconnect_code is None and token is None: @@ -493,13 +344,14 @@ class RoomConsumer(Consumer): self.member = waiter # await self.connect_self() self.add_to_group() + await self.connect_self() await self.direct_send(type="loggedIn", payload={"member": serialize_member(self.member).dict()}) return self.member = waiter self.add_to_group() await self.direct_send(type="waiting", payload={"waiter": serialize_member(self.member).dict()}) - await self.broadcast(type="waiter", payload={"waiter": serialize_member(self.member).dict()}) + await self.send_to_admin(type="waiter", payload={"waiter": serialize_member(self.member).dict()}) elif username is not None: waiter = create_anonymous_waiter(username, self.room, self.db) @@ -509,13 +361,15 @@ class RoomConsumer(Consumer): self.member = waiter self.add_to_group() await self.direct_send(type="waiting", payload={"waiter": serialize_member(self.member).dict()}) - await self.broadcast(type="waiter", payload={"waiter": serialize_member(self.member).dict()}) + await self.send_to_admin(type="waiter", payload={"waiter": serialize_member(self.member).dict()}) else: if token is not None: user = get_user_from_token(token, self.db) if user is None: + await self.direct_send(type="error", payload={"msg": "Utilisateur introuvable"}) return if user is False: + await self.direct_send(type="error", payload={"msg": "Token expired"}) return member = create_user_member(user, self.room, self.db) @@ -523,7 +377,8 @@ class RoomConsumer(Consumer): return self.member = member self.add_to_group() - await self.direct_send() + await self.broadcast(type="joined", payload={"member": serialize_member(self.member).dict()}, exclude=True) + await self.direct_send(type="accepted", payload={"member": serialize_member(self.member).dict()}) elif username is not None: member = create_anonymous_member(username, self.room, self.db) if member is None: @@ -531,55 +386,166 @@ class RoomConsumer(Consumer): return self.member = member self.add_to_group() - await self.direct_send() + + await self.broadcast(type="joined", payload={"member": serialize_member(self.member).dict()}, exclude=True) + await self.direct_send(type="accepted", payload={"member": serialize_member(self.member).dict()}) + + async def isAdminReceive(self): + is_admin = self.member is not None and self.member.is_admin == True + if not is_admin: + await self.direct_send(type="error", payload={"msg": "Vous n'avez pas la permission de faire ca"}) + return False + return True def isAdmin(self): return self.member is not None and self.member.is_admin == True - @Consumer.event('accept', conditions=[isAdmin]) + @Consumer.event('accept', conditions=[isAdminReceive]) async def accept(self, waiter_id: str): waiter = get_waiter(waiter_id, self.db) + if waiter is None: + await self.direct_send(type="error", payload={'msg': "Utilisateur introuvable"}) + return member = accept_waiter(waiter, self.db) - await self.manager.broadcast({"type": "accepted", "data": {'member': serialize_member(member).dict()}}, f"waiter__{waiter_id}") + await self.send_to(type="accepted", payload={"member": serialize_member(member).dict()}, member_id=waiter_id) await self.broadcast(type="joined", payload={"member": serialize_member(member).dict()}) - - @Consumer.event('refuse', conditions=[isAdmin]) + + @Consumer.event('refuse', conditions=[isAdminReceive]) async def accept(self, waiter_id: str): waiter = get_waiter(waiter_id, self.db) member = refuse_waiter(waiter, self.db) - await self.manager.broadcast({"type": "refused", "data": {'waiter_id': waiter_id}}, f"waiter__{waiter_id}") - #await self.broadcast(type="joined", payload={"member": serialize_member(member).dict()}) + await self.send_to(type="refused", payload={'waiter_id': waiter_id}, member_id=waiter_id) + # await self.broadcast(type="joined", payload={"member": serialize_member(member).dict()}) @Consumer.event('ping_room') async def proom(self): - await self.broadcast(type='ping', payload={}) + await self.broadcast(type='ping', payload={}, exclude=True) + async def hasRoom(self): + if self.member is None: + await self.direct_send(type="error", payload={"msg": "Vous n'êtes connecté à aucune salle"}) + return self.member + @Consumer.event('leave', conditions=[hasRoom]) + async def leave(self): + if self.member.is_admin is True: + await self.direct_send(type="error", payload={"msg": "Vous ne pouvez pas quitter une salle dont vous êtes l'administrateur"}) + return + member_obj = serialize_member(self.member).dict() + leave_room(self.member, self.db) + await self.direct_send(type="successfully_leaved", payload = {}) + await self.broadcast(type='leaved', payload ={"member": member_obj}) + self.member = None + + @Consumer.event('ban', conditions=[isAdminReceive]) + async def ban(self, member_id: str): + member = get_member(member_id, self.room.id, self.db) + if member == None: + await self.direct_send(type="error", payload={"msg": "Utilisateur introuvable"}) + return + if member.is_admin is True: + await self.direct_send(type="error", payload={"msg": "Vous ne pouvez pas bannir un administrateur"}) + return + member_serialized = serialize_member(member) + leave_room(member, self.db) + await self.send_to(type="banned", payload={}, member_id=member.id_code) + await self.broadcast(type="leaved", payload = {"member": member_serialized.dict()}) + def isMember(self): return self.member is not None and self.member.waiting == False # Sending Events @Consumer.sending("joined", conditions=[isMember]) def joined(self, member: MemberRead): - print('MEMBER', self.member, member) - if (self.member.user is not None and member.username == self.member.user.username) or (self.member.anonymous and member.reconnect_code == self.member.anonymous.reconnect_code): - raise ValueError("Nope") + if self.member.id_code == member.id_code: + raise ValueError("") if self.member.is_admin == False: member.reconnect_code = "" return {"member": member} - @Consumer.sending('waiter', conditions=[isAdmin]) + @Consumer.sending(['waiter', "disconnect_waiter"], conditions=[isAdmin]) def waiter(self, waiter: Waiter): return {"waiter": waiter} + + @Consumer.sending(['connect', "disconnect", ""]) + def connect_event(self, member: MemberRead): + if not self.member.is_admin: + member.reconnect_code = "" + return {"member": member} + + @Consumer.sending('disconnect') + def disconnect_event(self, member: MemberRead): + if not self.member.is_admin: + member.reconnect_code = "" + return {"member": member} + + @Consumer.sending('disconnect_waiter', conditions=[isAdmin]) + def disconnect_event(self, waiter: Waiter): + return {"waiter": waiter} + def isWaiter(self): + return self.member is not None and self.member.waiting == True + + @Consumer.sending("refused", conditions=[isWaiter]) + def refused(self, waiter_id: str): + self.member = None + self.manager.remove(self.room.id, self) + return {"waiter_id": waiter_id} + + @Consumer.sending("banned", conditions=[isMember]) + def banned(self): + self.member = None + self.manager.remove(self.room.id, self) + return {} @Consumer.sending('ping', conditions=[isMember]) def ping(self): return {} - async def disconnect(self): + print("DISCONNECT", self.member) self.manager.remove(self.room.id, self) - #await self.disconnect_self() + await self.disconnect_self() + +class RoomManager: + def __init__(self): + self.active_connections: Dict[str, List[RoomConsumer]] = {} + + def add(self, group: str, ws: RoomConsumer): + + if group not in self.active_connections: + self.active_connections[group] = [] + #print("adding", ws, self.active_connections[group]) + if ws not in self.active_connections[group]: + #print('ACTUALLY ADDING') + self.active_connections[group].append(ws) + + def remove(self, group: str, ws: RoomConsumer): + if group in self.active_connections: + if ws in self.active_connections[group]: + self.active_connections[group].remove(ws) + + async def broadcast(self, message, group: str, exclude: list[RoomConsumer] = []): + if group in self.active_connections: + #print(self.active_connections[group], exclude) + for connection in list(set(self.active_connections[group])): + if connection not in exclude: + #print('SEND TO', connection, message) + await connection.send(message) + + async def send_to(self, group, id_code, msg): + if group in self.active_connections: + members = [c for c in self.active_connections[group] if c.member.id_code == id_code] + for m in members: + await m.send(msg) + + async def send_to_admin(self, group, msg): + if group in self.active_connections: + members = [c for c in self.active_connections[group] if c.member.is_admin == True] + for m in members: + await m.send(msg) + +manager = RoomManager() + @router.websocket('/ws/room/{room_id}') async def room_ws(ws: WebSocket, room: Room | None = Depends(check_room), db: Session = Depends(get_session)): if room is None: @@ -587,32 +553,3 @@ async def room_ws(ws: WebSocket, room: Room | None = Depends(check_room), db: Se status_code=status.HTTP_404_NOT_FOUND, detail='Room not found') consumer = RoomConsumer(ws=ws, room=room, manager=manager, db=db) await consumer.run() - - -class TestConsumer(Consumer): - async def connect(self): - await self.ws.accept() - - def test(self): - return True - - @Consumer.event("test", conditions=[True, test]) - async def testering(self): - await self.ws.send_json({"type": "success"}) - await self.send({"type": "test", "data": {"i": {"username": "lilian", "reconnect_code": "something", "isAdmin": False, "isUser": False}, "test": 12}}) - # await self.send({"type": "test", "data": {"i": {"username": "lilian", "reconnect_code": "something", "isAdmin": False, "isUser": False}}}) - return - - @Consumer.sending('test', conditions=[]) - def sendtest(self, i: MemberRead, test: int): - print("i", i) - print(i.reconnect_code) - print(dict(i)) - i.reconnect_code = "nope" - return {"i": i, "test": test} - - -@router.websocket('/ws/test') -async def test(ws: WebSocket): - consumer = TestConsumer(ws) - await consumer.run() diff --git a/backend/api/tests/test_room.py b/backend/api/tests/test_room.py index 7583213..93a6781 100644 --- a/backend/api/tests/test_room.py +++ b/backend/api/tests/test_room.py @@ -5,12 +5,13 @@ from tests.test_auth import test_register def test_create_room_no_auth(client: TestClient, public=False): r = client.post('/room', json={"name": "test_room", - "public": False}, params={'username': "lilian", "public": public}) + "public": public}, params={'username': "lilian"}) print(r.json()) assert "id_code" in r.json()['room'] assert r.json()['member']['reconnect_code'] is not None - assert {"room": {**r.json()['room'], 'id_code': None}, "member": {**r.json()['member'], "reconnect_code": None}} == {"room": {"id_code": None, "name": "test_room", - "public": False}, 'member': {"username": "lilian", "reconnect_code": None, "isUser": False, "isAdmin": True}} + assert "id_code" in r.json()['member'] + assert {"room": {**r.json()['room'], 'id_code': None}, "member": {**r.json()['member'], "reconnect_code": None, "id_code": None}} == {"room": {"id_code": None, "name": "test_room", + "public": public}, 'member': {"username": "lilian", "reconnect_code": None, "isUser": False, "isAdmin": True, "id_code": None}} return r.json() @@ -29,8 +30,9 @@ def test_create_room_auth(client: TestClient, token=None): "public": False}, headers={"Authorization": "Bearer " + token}) print(r.json()) assert "id_code" in r.json()['room'] + assert "id_code" in r.json()['member'] assert {**r.json(), "room": {**r.json()['room'], 'id_code': None}} == {"room": {"id_code": None, "name": "test_room", - "public": False}, 'member': {"username": "lilian", "reconnect_code": "", "isUser": True, "isAdmin": True}} + "public": False}, 'member': {"username": "lilian", "reconnect_code": "", "isUser": True, "isAdmin": True, "id_code": r.json()['member']['id_code']}} return r.json() @@ -51,9 +53,9 @@ def test_login_no_auth(client: TestClient): ws.send_json({"type": "login", "data": { "reconnect_code": member['reconnect_code']}}) data = ws.receive_json() - print(data) + assert "id_code" in data['data']['member'] assert data == {'type': "loggedIn", "data": {"member": { - "username": member['username'], "reconnect_code": member['reconnect_code'], "isAdmin": True, "isUser": False}}} + "username": member['username'], "reconnect_code": member['reconnect_code'], "isAdmin": True, "isUser": False, "id_code": data['data']["member"]["id_code"]}}} def test_login_no_auth_not_in_room(client: TestClient): @@ -76,8 +78,9 @@ def test_login_auth(client: TestClient): ws.send_json({"type": "login", "data": {"token": token}}) data = ws.receive_json() print(data) + assert "id_code" in data["data"]['member'] assert data == {'type': "loggedIn", "data": {"member": { - "username": member['username'], "isAdmin": True, "isUser": True, 'reconnect_code': ""}}} + "username": member['username'], "isAdmin": True, "isUser": True, 'reconnect_code': "", "id_code": data['data']["member"]["id_code"]}}} def test_login_auth_not_in_room(client: TestClient): @@ -103,10 +106,12 @@ def test_join_auth(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as member: member.send_json({"type": "join", "data": {"token": token}}) mdata = member.receive_json() + assert "waiter_id" in mdata['data']['waiter'] assert mdata == {"type": "waiting", "data": {"waiter": { "username": "lilian2", "waiter_id": mdata['data']['waiter']['waiter_id']}}} - + + admin.send_json({"type": "ping_room"}) adata = admin.receive_json() assert adata == {'type': "waiter", 'data': { "waiter": {"waiter_id": mdata['data']['waiter']['waiter_id'], "username": "lilian2"}}} @@ -114,14 +119,26 @@ def test_join_auth(client: TestClient): admin.send_json({"type": "accept", "data": { "waiter_id": mdata['data']['waiter']['waiter_id']}}) mdata = member.receive_json() + print('MDATA', mdata) assert mdata == {"type": "accepted", "data": {"member": { - "username": "lilian2", "isUser": True, "isAdmin": False, "reconnect_code": ""}}} + "username": "lilian2", "isUser": True, "isAdmin": False, "reconnect_code": "", "id_code": mdata['data']["member"]["id_code"]}}} adata = admin.receive_json() assert adata == {'type': "joined", 'data': { - "member": {"reconnect_code": "", "username": "lilian2", "isUser": True, "isAdmin": False}}} + "member": {"reconnect_code": "", "username": "lilian2", "isUser": True, "isAdmin": False, "id_code": adata['data']["member"]["id_code"]}}} admin.send_json({"type": "ping_room"}) mdata = member.receive_json() assert mdata == {"type": "ping", "data": {}} +def test_join_waiter_not_found(client: TestClient): + token = test_register(client, username="lilian2")['access'] + room = test_create_room_no_auth(client=client) + member = room['member'] + with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: + admin.send_json({"type": "login", "data": { + "reconnect_code": member['reconnect_code']}}) + admin.receive_json() + admin.send_json({"type": "accept", "data": {"waiter_id": "OOOO"}}) + data = admin.receive_json() + assert data == {"type": "error", "data": {"msg": "Utilisateur introuvable"}} def test_join_no_auth(client: TestClient): room = test_create_room_no_auth(client=client) @@ -148,15 +165,15 @@ def test_join_no_auth(client: TestClient): new_reconnect = mdata['data']['member']['reconnect_code'] assert 'reconnect_code' in mdata['data']['member'] assert mdata == {"type": "accepted", "data": {"member": { - "username": "member", "reconnect_code": new_reconnect, "isUser": False, "isAdmin": False}}} + "username": "member", "reconnect_code": new_reconnect, "isUser": False, "isAdmin": False, "id_code": mdata['data']["member"]["id_code"]}}} adata = admin.receive_json() assert adata == {'type': "joined", 'data': { - "member": {"reconnect_code": new_reconnect, "username": "member", "isUser": False, "isAdmin": False}}} + "member": {"reconnect_code": new_reconnect, "username": "member", "isUser": False, "isAdmin": False, "id_code": adata['data']["member"]["id_code"]}}} admin.send_json({"type": "ping_room"}) mdata = memberws.receive_json() assert mdata == {"type": "ping", "data": {}} - return {"room": room, "members": [member['reconnect_code'], new_reconnect]} + return {"room": room['room'], "members": [member['reconnect_code'], new_reconnect]} def test_join_no_auth_username_error(client: TestClient): @@ -217,7 +234,7 @@ def test_join_auth_in_room_yet(client: TestClient): member.send_json({"type": "join", "data": {"token": token}}) mdata = member.receive_json() assert mdata == {"type": "loggedIn", "data": {"member": { - "username": "lilian2", "isAdmin": True, "isUser": True}}} + "username": "lilian", "isAdmin": True, "isUser": True, 'reconnect_code': "", "id_code": mdata['data']["member"]["id_code"]}}} def test_join_auth_public(client: TestClient): @@ -227,19 +244,19 @@ def test_join_auth_public(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { "reconnect_code": member['reconnect_code']}}) - + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as member: member.send_json({"type": "join", "data": {"token": token}}) mdata = member.receive_json() assert mdata == {"type": "accepted", "data": {"member": { - "username": "member", "isUser": True, "isAdmin": False}}} + "username": "lilian2", "isUser": True, "isAdmin": False, "reconnect_code": "", "id_code": mdata['data']["member"]["id_code"]}}} adata = admin.receive_json() assert adata == {'type': "joined", 'data': { - "member": {"reconnect_code": "", "username": "member", "isUser": True}}} + "member": {"reconnect_code": "", "username": "lilian2", "isUser": True, "isAdmin": False, "id_code": mdata['data']["member"]["id_code"]}}} admin.send_json({"type": "ping_room"}) mdata = member.receive_json() - assert mdata == {"type": "ping"} + assert mdata == {"type": "ping", "data": {}} def test_join_no_auth_public(client: TestClient): @@ -248,46 +265,46 @@ def test_join_no_auth_public(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { "reconnect_code": member['reconnect_code']}}) - + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as member: member.send_json({"type": "join", "data": {"username": "member"}}) mdata = member.receive_json() assert 'reconnect_code' in mdata['data']['member'] assert mdata == {"type": "accepted", "data": {"member": { - "username": "member", "reconnect_code": mdata['data']['reconnect_code'], "isUser": False, "isAdmin": False}}} + "username": "member", "reconnect_code": mdata['data']['member']['reconnect_code'], "isUser": False, "isAdmin": False, "id_code": mdata['data']["member"]["id_code"]}}} adata = admin.receive_json() assert adata == {'type': "joined", 'data': { - "member": {"reconnect_code": mdata['data']['reconnect_code'], "username": "member", "isUser": False}}} + "member": {"reconnect_code": mdata['data']['member']['reconnect_code'], "username": "member", "isUser": False, "isAdmin": False, "id_code": mdata['data']["member"]["id_code"]}}} member.send_json({"type": "update_groups"}) admin.send_json({"type": "ping_room"}) mdata = member.receive_json() - assert mdata == {"type": "ping"} + assert mdata == {"type": "ping", "data": {}} -def test_join_no_auth_unauthorized(client: TestClient): +def test_join_auth_unauthorized(client: TestClient): token = test_register(client, username="lilian2")['access'] room = test_create_room_no_auth(client=client) member = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { "reconnect_code": member['reconnect_code']}}) - + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as member: member.send_json({"type": "join", "data": {"token": token}}) mdata = member.receive_json() - assert "id_code" in mdata['data']['waiter'] + assert "waiter_id" in mdata['data']['waiter'] assert mdata == {"type": "waiting", "data": {"waiter": { - "username": "lilian2", "id_code": mdata['data']['waiter']}}} + "username": "lilian2", "waiter_id": mdata['data']['waiter']['waiter_id']}}} adata = admin.receive_json() assert adata == {'type': "waiter", 'data': { - "waiter": {"id_code": mdata['data']['waiter']['id_code'], "username": "member"}}} + "waiter": {"waiter_id": mdata['data']['waiter']['waiter_id'], "username": "lilian2"}}} member.send_json({"type": "refuse", "data": { - "waiter_id": mdata['data']['waiter']['id_code']}}) + "waiter_id": mdata['data']['waiter']['waiter_id']}}) mdata = member.receive_json() assert mdata == {"type": "error", "data": { @@ -300,14 +317,14 @@ def test_connect_admin(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { "reconnect_code": members[0]}}) - + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as member: member.send_json({'type': "login", "data": { - "reconnect_code": members[0]}}) + "reconnect_code": members[1]}}) adata = admin.receive_json() assert adata == {"type": "connect", "data": {"member": { - "username": "member", "reconnect_code": members[1], "isAdmin": False, "isUser": False}}} + "username": "member", "reconnect_code": members[1], "isAdmin": False, "isUser": False, "id_code": adata['data']["member"]["id_code"]}}} def test_connect_member(client: TestClient): @@ -316,15 +333,14 @@ def test_connect_member(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: memberws.send_json({"type": "login", "data": { "reconnect_code": members[1]}}) - + memberws.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({'type': "login", "data": { "reconnect_code": members[0]}}) mdata = memberws.receive_json() assert mdata == {"type": "connect", "data": {"member": { - "username": "member", "reconnect_code": "", "isAdmin": True, "isUser": False}}} - + "username": "lilian", "reconnect_code": "", "isAdmin": True, "isUser": False, "id_code": mdata['data']["member"]["id_code"]}}} def test_disconnect(client: TestClient): room = test_join_no_auth(client=client) @@ -332,42 +348,61 @@ def test_disconnect(client: TestClient): with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: memberws.send_json({"type": "login", "data": { "reconnect_code": members[1]}}) - + memberws.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({'type': "login", "data": { "reconnect_code": members[0]}}) - + memberws.receive_json() admin.close() mdata = memberws.receive_json() assert mdata == {"type": "disconnect", "data": {"member": { - "username": "member", "reconnect_code": "", "isAdmin": True, "isUser": False}}} + "username": "lilian", "reconnect_code": "", "isAdmin": True, "isUser": False, "id_code": mdata['data']["member"]["id_code"]}}} +def test_disconnect_waiter(client: TestClient): + room = test_create_room_no_auth(client=client) + member = room['member'] + + with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: + admin.send_json({"type": "login", "data": { + "reconnect_code": member['reconnect_code']}}) + admin.receive_json() + with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: + memberws.send_json({'type': "join", "data": { + "username": "test"}}) + mdata = memberws.receive_json() + admin.receive_json() + memberws.close() + + adata = admin.receive_json() + assert adata == {"type": "disconnect_waiter", "data": {"waiter": {**mdata['data']['waiter']}}} def test_leave(client: TestClient): - room = test_join_no_auth(client=client) - members = room['members'] + room = test_create_room_no_auth(client=client, public=True) + member = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) + "reconnect_code": member["reconnect_code"]}}) + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: - memberws.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) + memberws.send_json({"type": "join", "data": { + "username":"test"}}) + m = memberws.receive_json() + admin.receive_json() memberws.send_json({"type": "leave"}) data = memberws.receive_json() - assert data == {"type": "successfully_leaved"} + assert data == {"type": "successfully_leaved", "data": {}} adata = admin.receive_json() assert adata == {"type": "leaved", "data": {"member": { - "username": "member", "reconnect_code": members[1], "isAdmin": False, "isUser": False}}} + "username": "test", "reconnect_code": m["data"]['member']["reconnect_code"], "isAdmin": False, "isUser": False, "id_code": adata['data']["member"]["id_code"]}}} def test_leave_not_connected(client: TestClient): room = test_create_room_no_auth(client=client) - members = room['members'] + members = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: - memberws.send_json({"type": "leave"}) data = memberws.receive_json() assert data == {"type": "error", "data": { @@ -375,12 +410,13 @@ def test_leave_not_connected(client: TestClient): def test_leave_admin(client: TestClient): - room = test_join_no_auth(client=client) - members = room['members'] + room = test_create_room_no_auth(client=client) + member = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { - "reconnect_code": members[0]}}) + "reconnect_code": member["reconnect_code"]}}) + admin.receive_json() admin.send_json({"type": "leave"}) data = admin.receive_json() assert data == {"type": "error", "data": { @@ -388,50 +424,74 @@ def test_leave_admin(client: TestClient): def test_ban_anonymous(client: TestClient): - room = test_create_room_no_auth(client=client) - members = room['members'] + room = test_create_room_no_auth(client=client, public=True) + member = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) + "reconnect_code": member['reconnect_code']}}) + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: - memberws.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) - admin.send_json({"type": "ban", "data": {"member": { - "username": "member", "reconnect_code": members[1], "isUser": False, "isAdmin": False}}}) + memberws.send_json({"type": "join", "data": { + "username": "test"}}) + m = memberws.receive_json() + reconnect_code = m['data']['member']['reconnect_code'] + id_code = m['data']['member']['id_code'] + admin.receive_json() + admin.send_json({"type": "ban", "data": {"member_id": m['data']['member']['id_code']}}) + mdata = memberws.receive_json() + assert mdata == {"type": "banned", "data": {}} + adata = admin.receive_json() assert adata == {"type": "leaved", "data": {"member": { - "username": "member", "reconnect_code": members[1], "isUser": True, "isAdmin": False}}} + "username": "test", "reconnect_code": reconnect_code, "isUser": False, "isAdmin": False, "id_code": id_code}}} def test_ban_anonymous_unauthorized(client: TestClient): - room = test_create_room_no_auth(client=client) - members = room['members'] + room = test_create_room_no_auth(client=client, public=True) + members = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) + "reconnect_code": members["reconnect_code"]}}) + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: - memberws.send_json({"type": "login", "data": { - "reconnect_code": members[1]}}) - memberws.send_json({"type": "ban", "data": {"member": { - "username": "member", "reconnect_code": members[1], "isUser": False, "isAdmin": False}}}) + memberws.send_json({"type": "join", "data": { + "username": "test"}}) + memberws.receive_json() + memberws.send_json({"type": "ban", "data": {"member_id": "OOO"}}) mdata = memberws.receive_json() assert mdata == {"type": "error", "data": { - "msg": "Vous n'avez pas les permissions pour faire ça"}} - - -def test_ban_user(client: TestClient): - token = test_register(client=client, username="lilian2") + "msg": "Vous n'avez pas la permission de faire ca"}} +def test_ban_admin(client: TestClient): room = test_create_room_no_auth(client=client) members = room['member'] with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: admin.send_json({"type": "login", "data": { - "reconnect_code": members["reconnect_code "]}}) + "reconnect_code": members["reconnect_code"]}}) + data = admin.receive_json() + admin.send_json({"type": "ban", "data": {"member_id": data['data']['member']['id_code']}}) + a = admin.receive_json() + assert a == {'type': "error", "data": { + "msg": "Vous ne pouvez pas bannir un administrateur"}} + + +def test_ban_user(client: TestClient): + token = test_register(client=client, username="lilian2")['access'] + room = test_create_room_no_auth(client=client, public=True) + member = room['member'] + with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as admin: + admin.send_json({"type": "login", "data": { + "reconnect_code": member["reconnect_code"]}}) + admin.receive_json() with client.websocket_connect(f"/ws/room/" + room['room']['id_code']) as memberws: memberws.send_json({"type": "join", "data": {"token": token}}) - adata = admin.receive_json() + m = memberws.receive_json() + admin.receive_json() + + admin.send_json( + {"type": "ban", "data": {"member_id": m['data']['member']['id_code']}}) + mdata = memberws.receive_json() + assert mdata == {"type": "banned", "data": {}} - admin.send_json({"type": "ban", "data": {"member": { - "username": "lilian2", "reconnect_code": "", "isUser": True, "isAdmin": False}}}) adata = admin.receive_json() assert adata == {"type": "leaved", "data": {"member": { - "username": "lilian2", "reconnect_code": "", "isUser": True, "isAdmin": False}}} + "username": "lilian2", "reconnect_code": "", "isUser": True, "isAdmin": False, "id_code": m['data']['member']['id_code']}}}