feat(chat): support public channel chat

This commit is contained in:
MingxuanGame
2025-08-16 05:29:16 +00:00
parent 9a5c2fde08
commit f992e4cc71
13 changed files with 925 additions and 3 deletions

138
app/router/chat/channel.py Normal file
View File

@@ -0,0 +1,138 @@
from __future__ import annotations
from typing import Any
from app.database.chat import (
ChannelType,
ChatChannel,
ChatChannelResp,
)
from app.database.lazer_user import User, UserResp
from app.dependencies.database import get_db, get_redis
from app.dependencies.user import get_current_user
from app.router.v2 import api_v2_router as router
from .server import server
from fastapi import Depends, HTTPException, Query, Security
from pydantic import BaseModel, Field
from redis.asyncio import Redis
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession
class UpdateResponse(BaseModel):
presence: list[ChatChannelResp] = Field(default_factory=list)
silences: list[Any] = Field(default_factory=list)
@router.get("/chat/updates", response_model=UpdateResponse)
async def get_update(
history_since: int | None = Query(None),
since: int | None = Query(None),
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
includes: list[str] = Query(["presence"], alias="includes[]"),
redis: Redis = Depends(get_redis),
):
resp = UpdateResponse()
if "presence" in includes:
channel_ids = server.get_user_joined_channel(current_user.id)
for channel_id in channel_ids:
channel = await ChatChannel.get(channel_id, session)
if channel:
resp.presence.append(
await ChatChannelResp.from_db(
channel,
session,
server.channels.get(channel_id, []),
current_user,
redis,
)
)
return resp
@router.put("/chat/channels/{channel}/users/{user}", response_model=ChatChannelResp)
async def join_channel(
channel: str,
user: str,
current_user: User = Security(get_current_user, scopes=["chat.write_manage"]),
session: AsyncSession = Depends(get_db),
):
db_channel = await ChatChannel.get(channel, session)
if db_channel is None:
raise HTTPException(status_code=404, detail="Channel not found")
return await server.join_channel(current_user, db_channel, session)
@router.delete(
"/chat/channels/{channel}/users/{user}",
status_code=204,
)
async def leave_channel(
channel: str,
user: str,
current_user: User = Security(get_current_user, scopes=["chat.write_manage"]),
session: AsyncSession = Depends(get_db),
):
db_channel = await ChatChannel.get(channel, session)
if db_channel is None:
raise HTTPException(status_code=404, detail="Channel not found")
await server.leave_channel(current_user, db_channel, session)
return
@router.get("/chat/channels")
async def get_channel_list(
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
redis: Redis = Depends(get_redis),
):
channels = (
await session.exec(
select(ChatChannel).where(ChatChannel.type == ChannelType.PUBLIC)
)
).all()
results = []
for channel in channels:
assert channel.channel_id is not None
results.append(
await ChatChannelResp.from_db(
channel,
session,
server.channels.get(channel.channel_id, []),
current_user,
redis,
)
)
return results
class GetChannelResp(BaseModel):
channel: ChatChannelResp
users: list[UserResp] = Field(default_factory=list)
@router.get("/chat/channels/{channel}")
async def get_channel(
channel: str,
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
redis: Redis = Depends(get_redis),
):
db_channel = await ChatChannel.get(channel, session)
if db_channel is None:
raise HTTPException(status_code=404, detail="Channel not found")
assert db_channel.channel_id is not None
return GetChannelResp(
channel=await ChatChannelResp.from_db(
db_channel,
session,
server.channels.get(db_channel.channel_id, []),
current_user,
redis,
)
)