docs(chat): add API docs

This commit is contained in:
MingxuanGame
2025-08-16 15:35:27 +00:00
parent 4eace3f84e
commit 87a3928e20
4 changed files with 117 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import inspect
from typing import Any
from fastapi import Request
@@ -36,4 +37,15 @@ def BodyOrForm[T: BaseModel](model: type[T]):
except ValidationError as e:
raise RequestValidationError(e.errors())
dependency.__signature__ = inspect.signature( # pyright: ignore[reportFunctionMemberAccess]
lambda x: None
).replace(
parameters=[
inspect.Parameter(
name=model.__name__.lower(),
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=model,
)
]
)
return dependency

View File

@@ -11,8 +11,15 @@ from fastapi import Query
__all__ = ["chat_router"]
@router.get("/notifications")
async def get_notifications(max_id: int | None = Query(None)):
@router.get(
"/notifications",
tags=["通知", "聊天"],
name="获取通知",
description="获取当前用户未读通知。根据 ID 排序。同时返回通知服务器入口。",
)
async def get_notifications(
max_id: int | None = Query(None, description="获取 ID 小于此值的通知"),
):
if settings.server_url is not None:
notification_endpoint = f"{settings.server_url}notification-server".replace(
"http://", "ws://"

View File

@@ -18,7 +18,7 @@ from app.router.v2 import api_v2_router as router
from .server import server
from fastapi import Depends, HTTPException, Query, Security
from fastapi import Depends, HTTPException, Path, Query, Security
from pydantic import BaseModel, Field, model_validator
from redis.asyncio import Redis
from sqlmodel import col, select
@@ -30,13 +30,23 @@ class UpdateResponse(BaseModel):
silences: list[Any] = Field(default_factory=list)
@router.get("/chat/updates", response_model=UpdateResponse)
@router.get(
"/chat/updates",
response_model=UpdateResponse,
name="获取更新",
description="获取当前用户所在频道的最新的禁言情况。",
tags=["聊天"],
)
async def get_update(
history_since: int | None = Query(None),
since: int | None = Query(None),
history_since: int | None = Query(
None, description="获取自此禁言 ID 之后的禁言记录"
),
since: int | None = Query(None, description="获取自此消息 ID 之后的禁言记录"),
includes: list[str] = Query(
["presence", "silences"], alias="includes[]", description="要包含的更新类型"
),
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()
@@ -83,10 +93,16 @@ async def get_update(
return resp
@router.put("/chat/channels/{channel}/users/{user}", response_model=ChatChannelResp)
@router.put(
"/chat/channels/{channel}/users/{user}",
response_model=ChatChannelResp,
name="加入频道",
description="加入指定的公开/房间频道。",
tags=["聊天"],
)
async def join_channel(
channel: str,
user: str,
channel: str = Path(..., description="频道 ID/名称"),
user: str = Path(..., description="用户 ID"),
current_user: User = Security(get_current_user, scopes=["chat.write_manage"]),
session: AsyncSession = Depends(get_db),
):
@@ -100,10 +116,13 @@ async def join_channel(
@router.delete(
"/chat/channels/{channel}/users/{user}",
status_code=204,
name="离开频道",
description="将用户移出指定的公开/房间频道。",
tags=["聊天"],
)
async def leave_channel(
channel: str,
user: str,
channel: str = Path(..., description="频道 ID/名称"),
user: str = Path(..., description="用户 ID"),
current_user: User = Security(get_current_user, scopes=["chat.write_manage"]),
session: AsyncSession = Depends(get_db),
):
@@ -115,7 +134,13 @@ async def leave_channel(
return
@router.get("/chat/channels")
@router.get(
"/chat/channels",
response_model=list[ChatChannelResp],
name="获取频道列表",
description="获取所有公开频道。",
tags=["聊天"],
)
async def get_channel_list(
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
@@ -148,9 +173,15 @@ class GetChannelResp(BaseModel):
users: list[UserResp] = Field(default_factory=list)
@router.get("/chat/channels/{channel}")
@router.get(
"/chat/channels/{channel}",
response_model=GetChannelResp,
name="获取频道信息",
description="获取指定频道的信息。",
tags=["聊天"],
)
async def get_channel(
channel: str,
channel: str = Path(..., description="频道 ID/名称"),
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
redis: Redis = Depends(get_redis),
@@ -211,7 +242,13 @@ class CreateChannelReq(BaseModel):
return self
@router.post("/chat/channels")
@router.post(
"/chat/channels",
response_model=ChatChannelResp,
name="创建频道",
description="创建一个新的私聊/通知频道。如果存在私聊频道则重新加入。",
tags=["聊天"],
)
async def create_channel(
req: CreateChannelReq = Depends(BodyOrForm(CreateChannelReq)),
current_user: User = Security(get_current_user, scopes=["chat.write_manage"]),

View File

@@ -19,7 +19,7 @@ from app.router.v2 import api_v2_router as router
from .banchobot import bot
from .server import server
from fastapi import Depends, HTTPException, Query, Security
from fastapi import Depends, HTTPException, Path, Query, Security
from pydantic import BaseModel, Field
from redis.asyncio import Redis
from sqlmodel import col, select
@@ -30,10 +30,18 @@ class KeepAliveResp(BaseModel):
silences: list[UserSilenceResp] = Field(default_factory=list)
@router.post("/chat/ack")
@router.post(
"/chat/ack",
name="保持连接",
response_model=KeepAliveResp,
description="保持公共频道的连接。同时返回最近的禁言列表。",
tags=["聊天"],
)
async def keep_alive(
history_since: int | None = Query(None),
since: int | None = Query(None),
history_since: int | None = Query(
None, description="获取自此禁言 ID 之后的禁言记录"
),
since: int | None = Query(None, description="获取自此消息 ID 之后的禁言记录"),
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
):
@@ -68,9 +76,15 @@ class MessageReq(BaseModel):
uuid: str | None = None
@router.post("/chat/channels/{channel}/messages", response_model=ChatMessageResp)
@router.post(
"/chat/channels/{channel}/messages",
response_model=ChatMessageResp,
name="发送消息",
description="发送消息到指定频道。",
tags=["聊天"],
)
async def send_message(
channel: str,
channel: str = Path(..., description="频道 ID/名称"),
req: MessageReq = Depends(BodyOrForm(MessageReq)),
current_user: User = Security(get_current_user, scopes=["chat.write"]),
session: AsyncSession = Depends(get_db),
@@ -103,12 +117,18 @@ async def send_message(
return resp
@router.get("/chat/channels/{channel}/messages", response_model=list[ChatMessageResp])
@router.get(
"/chat/channels/{channel}/messages",
response_model=list[ChatMessageResp],
name="获取消息",
description="获取指定频道的消息列表。",
tags=["聊天"],
)
async def get_message(
channel: str,
limit: int = Query(50, ge=1, le=50),
since: int = Query(default=0, ge=0),
until: int | None = Query(None),
limit: int = Query(50, ge=1, le=50, description="获取消息的数量"),
since: int = Query(default=0, ge=0, description="获取自此消息 ID 之后的消息记录"),
until: int | None = Query(None, description="获取自此消息 ID 之前的消息记录"),
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
):
@@ -130,10 +150,16 @@ async def get_message(
return resp
@router.put("/chat/channels/{channel}/mark-as-read/{message}", status_code=204)
@router.put(
"/chat/channels/{channel}/mark-as-read/{message}",
status_code=204,
name="标记消息为已读",
description="标记指定消息为已读。",
tags=["聊天"],
)
async def mark_as_read(
channel: str,
message: int,
channel: str = Path(..., description="频道 ID/名称"),
message: int = Path(..., description="消息 ID"),
current_user: User = Security(get_current_user, scopes=["chat.read"]),
session: AsyncSession = Depends(get_db),
):
@@ -157,7 +183,12 @@ class NewPMResp(BaseModel):
new_channel_id: int
@router.post("/chat/new")
@router.post(
"/chat/new",
name="创建私聊频道",
description="创建一个新的私聊频道。",
tags=["聊天"],
)
async def create_new_pm(
req: PMReq = Depends(BodyOrForm(PMReq)),
current_user: User = Security(get_current_user, scopes=["chat.write"]),