from datetime import datetime from typing import Annotated import asyncpg from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel from utils.read import feed from utils.read.feed import CURSOR_PATTERN, FeedChatKind, FeedFilter, FeedKind from utils.read.models import DEFAULT_LIMIT, FeedMessagesPage, FeedPage, FeedUnseen router = APIRouter(prefix="/api/feed", tags=["feed"], route_class=DishkaRoute) AccountId = Annotated[int, Query()] Limit = Annotated[int, Query(ge=1)] Cursor = Annotated[str | None, Query(pattern=CURSOR_PATTERN)] class SeenRequest(BaseModel): account_id: int at: datetime | None = None @router.get("") async def list_feed( pool: FromDishka[asyncpg.Pool], account_id: AccountId, kind: Annotated[FeedKind, Query()] = FeedKind.ALL, chat_id: Annotated[int | None, Query()] = None, chat_kind: Annotated[FeedChatKind | None, Query()] = None, since: Annotated[datetime | None, Query()] = None, cursor: Cursor = None, limit: Limit = DEFAULT_LIMIT, ) -> FeedPage: return await feed.list_feed( pool, account_id, FeedFilter(kind=kind, chat_id=chat_id, chat_kind=chat_kind, since=since), limit=limit, cursor=cursor, ) @router.get("/unseen") async def feed_unseen( pool: FromDishka[asyncpg.Pool], account_id: AccountId ) -> FeedUnseen: return await feed.unseen(pool, account_id) @router.post("/seen") async def feed_seen(pool: FromDishka[asyncpg.Pool], body: SeenRequest) -> FeedUnseen: return await feed.mark_seen(pool, body.account_id, body.at) @router.get("/{item_id}/messages") async def feed_item_messages( pool: FromDishka[asyncpg.Pool], item_id: int, account_id: AccountId, cursor: Cursor = None, limit: Limit = DEFAULT_LIMIT, ) -> FeedMessagesPage: page = await feed.item_messages( pool, account_id, item_id, limit=limit, cursor=cursor ) if page is None: raise HTTPException(status_code=404, detail="Feed item not found") return page