Skip to content

Commit

Permalink
Merge pull request #8 from sinasezza/sinasezza
Browse files Browse the repository at this point in the history
feat(socket): join rooms and send messages in public and private room…
  • Loading branch information
sinasezza authored Aug 3, 2024
2 parents d4a3e96 + 4e7cfbd commit 801b21a
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 12 deletions.
6 changes: 3 additions & 3 deletions chatApp/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class Settings(BaseSettings):
refresh_token_expire_days: int = Field(default=14)

# CORS settings
cors_allow_origins: list[str] = Field(default=["*"])
cors_allow_origins: list[str] | str = Field(default=["*"])
cors_allow_credentials: bool = Field(default=True)
cors_allow_methods: list[str] = Field(default=["*"])
cors_allow_headers: list[str] = Field(default=["*"])
cors_allow_methods: list[str] | str = Field(default=["*"])
cors_allow_headers: list[str] | str = Field(default=["*"])

# Trusted hosts settings
trusted_hosts: list[str] = Field(default=["127.0.0.1", "localhost"])
Expand Down
5 changes: 5 additions & 0 deletions chatApp/config/database.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from functools import lru_cache

from motor.motor_asyncio import (
AsyncIOMotorClient,
Expand Down Expand Up @@ -59,6 +60,7 @@ async def close_mongodb_connection(self) -> None:
mongo_db = MongoDB()


@lru_cache
def get_users_collection() -> AsyncIOMotorCollection:
"""
Retrieve the users collection from the MongoDB database.
Expand All @@ -72,6 +74,7 @@ def get_users_collection() -> AsyncIOMotorCollection:
return users_collection


@lru_cache
def get_messages_collection() -> AsyncIOMotorCollection:
"""
Retrieve the messages collection from the MongoDB database.
Expand All @@ -85,6 +88,7 @@ def get_messages_collection() -> AsyncIOMotorCollection:
return messages_collection


@lru_cache
def get_public_rooms_collection() -> AsyncIOMotorCollection:
"""
Retrieve the public rooms collection from the MongoDB database.
Expand All @@ -98,6 +102,7 @@ def get_public_rooms_collection() -> AsyncIOMotorCollection:
return rooms_collection


@lru_cache
def get_private_rooms_collection() -> AsyncIOMotorCollection:
"""
Retrieve the private rooms collection from the MongoDB database.
Expand Down
6 changes: 3 additions & 3 deletions chatApp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ async def shutdown_event():
)

# Include your routers for API endpoints
app.include_router(auth.router, prefix="/auth")
app.include_router(chat.router, prefix="/chat")
app.include_router(user.router, prefix="/user")
app.include_router(auth.router, prefix="/auth", tags=["auth"])
app.include_router(chat.router, prefix="/chat", tags=["chat"])
app.include_router(user.router, prefix="/user", tags=["user"])


@app.get("/")
Expand Down
42 changes: 40 additions & 2 deletions chatApp/models/message.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
from collections.abc import Mapping
from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field

from chatApp.config.database import get_messages_collection
from chatApp.utils.object_id import PydanticObjectId

from .public_room import fetch_public_room_by_id


class Message(BaseModel):
user_id: PydanticObjectId
room_id: PydanticObjectId
content: str = Field(default=None)
media: str = Field(default=None)
room_type: str
content: str | None = Field(default=None)
media: str | None = Field(default=None)
created_at: datetime = Field(default_factory=lambda: datetime.now())


class MessageInDB(Message):
id: PydanticObjectId = Field(alias="_id", serialization_alias="id")


# async def insert_message(message: str):
# message_collection = get_messages_collection()
# result = await message_collection.insert_one(message.dict())
# return result.inserted_id


async def get_public_messages(
room_id: str,
) -> tuple[bool, list[Mapping[str, Any]]]:
"""
Fetch public messages from a specific room.
:param room_id: The ID of the public room to fetch messages from.
:return: A tuple where the first element is a boolean indicating success,
and the second element is a list of messages (each message represented as a dictionary).
"""
messages_collection = get_messages_collection()

# Fetch the public room by ID
room = await fetch_public_room_by_id(room_id)
if room is None:
return False, []

# Fetch messages from the messages collection
cursor = messages_collection.find({"room_id": room_id})
messages = await cursor.to_list(
length=None
) # Await the cursor to list conversion

return True, messages
42 changes: 42 additions & 0 deletions chatApp/models/private_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pydantic import BaseModel, Field

from chatApp.config.database import get_private_rooms_collection
from chatApp.utils.object_id import PydanticObjectId


Expand All @@ -13,3 +14,44 @@ class PrivateRoom(BaseModel):

class PrivateRoomInDB(PrivateRoom):
id: PydanticObjectId = Field(alias="_id")


async def fetch_private_room_by_id(id: str):
room_collection = get_private_rooms_collection()
return await room_collection.find_one({"_id": PydanticObjectId(id)})


async def check_members_in_room(user1_id: str, user2_id: str):
rooms_collection = get_private_rooms_collection()
room = await rooms_collection.find_one(
{
"$or": [
{
"member1": PydanticObjectId(user1_id),
"member2": PydanticObjectId(user2_id),
},
{
"member1": PydanticObjectId(user2_id),
"member2": PydanticObjectId(user1_id),
},
],
}
)
return str(room["_id"]) if room is not None else None


async def join_private_room(user1_id: str, user2_id: str) -> str:
rooms_collection = get_private_rooms_collection()

room_id = await check_members_in_room(user1_id, user2_id)

if room_id is not None:
return room_id
else:
room = await rooms_collection.insert_one(
{
"member1": PydanticObjectId(user1_id),
"member2": PydanticObjectId(user2_id),
}
)
return str(room.inserted_id)
38 changes: 38 additions & 0 deletions chatApp/models/public_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pydantic import BaseModel, Field

from chatApp.config.database import get_public_rooms_collection
from chatApp.utils.object_id import PydanticObjectId


Expand Down Expand Up @@ -41,3 +42,40 @@ class PublicRoom(BaseModel):

class PublicRoomInDB(PublicRoom):
id: PydanticObjectId = Field(alias="_id", serialization_alias="id")


async def fetch_public_room_by_id(id: str):
room_collection = get_public_rooms_collection()
return await room_collection.find_one({"_id": PydanticObjectId(id)})


async def join_public_room(room_id: str, user_id: str) -> bool:
rooms_collection = get_public_rooms_collection()
room = await rooms_collection.find_one({"_id": PydanticObjectId(room_id)})

# Ensure room is not None
if room is None:
return False

# Define the expected structure of the room dictionary
# Adjust this according to the actual structure
ban_list: list[PydanticObjectId] = room.get("ban_list", [])
members: list[PydanticObjectId] = room.get("members", [])

userObjId = PydanticObjectId(user_id)
if userObjId not in ban_list:
if userObjId not in members:
members.append(userObjId)

# Update the room in the database
result = await rooms_collection.update_one(
{"_id": PydanticObjectId(room_id)},
{"$set": {"members": members}}, # Update only members
)

if result.modified_count == 1:
return True
else:
return False # Failed to update the room in the database
return True # User is already a member
return False # User is banned
36 changes: 35 additions & 1 deletion chatApp/routes/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from chatApp.models.public_room import PublicRoom, PublicRoomInDB
from chatApp.models.user import UserInDB
from chatApp.schemas.private_room import CreatePrivateRoom
from chatApp.schemas.public_room import CreatePublicRoom
from chatApp.schemas.public_room import CreatePublicRoom, GetPulbicRoomsSchema
from chatApp.utils.object_id import PydanticObjectId, is_valid_object_id

router = APIRouter()
Expand Down Expand Up @@ -75,6 +75,40 @@ async def join_public_room(
return PublicRoomInDB(**room)


@router.get("/get-public-rooms/", response_model=Mapping[str, Any])
async def get_public_rooms(
page: int = 1,
per_page: int = 10,
):
rooms_collection: AsyncIOMotorCollection = get_public_rooms_collection()
total_count = await rooms_collection.count_documents({})
rooms = (
await rooms_collection.find(
{},
{
"_id": 1,
"name": 1,
"description": 1,
"owner": 1,
"created_at": 1,
},
)
.skip((page - 1) * per_page)
.limit(per_page)
.to_list(None)
)

data_to_return = {
"data": [GetPulbicRoomsSchema(**room) for room in rooms],
"meta": {
"total_count": total_count,
"page": page,
"per_page": per_page,
},
}
return data_to_return


@router.post(
"/create-private-room/{person_id}", response_model=PrivateRoomInDB
)
Expand Down
17 changes: 17 additions & 0 deletions chatApp/schemas/public_room.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from datetime import datetime

from pydantic import BaseModel, Field

from chatApp.utils.object_id import PydanticObjectId


class CreatePublicRoom(BaseModel):
name: str = Field(..., description="Name of the public room")
Expand All @@ -20,3 +24,16 @@ class CreatePublicRoom(BaseModel):
max_latest_messages_access: int | None = Field(
None, description="Maximum number of latest messages to access"
)


class GetPulbicRoomsSchema(BaseModel):
id: PydanticObjectId = Field(
...,
description="id of the room",
alias="_id",
serialization_alias="id",
)
owner: PydanticObjectId = Field(..., description="id of owner of the room")
name: str
description: str | None = None
created_at: datetime
Loading

0 comments on commit 801b21a

Please sign in to comment.