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.jobs import enqueue from utils.read import social from utils.read.models import CallbackView, LinkView, ReactionView, ReadInfoView from utils.read.read_receipts import read_info router = APIRouter( prefix="/api/messages/{chat_id}/{message_id}", tags=["social"], route_class=DishkaRoute, ) AccountId = Annotated[int, Query()] class RefreshRequest(BaseModel): account_id: int @router.get("/callbacks") async def message_callbacks( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, account_id: AccountId ) -> list[CallbackView]: return await social.get_callbacks(pool, account_id, chat_id, message_id) @router.get("/reactions") async def message_reactions( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, account_id: AccountId ) -> list[ReactionView]: return await social.get_reactions(pool, account_id, chat_id, message_id) @router.get("/links") async def message_links( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, account_id: AccountId ) -> list[LinkView]: return await social.get_links(pool, account_id, chat_id, message_id) @router.get("/read") async def message_read( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, account_id: AccountId ) -> ReadInfoView: info = await read_info(pool, account_id, chat_id, message_id) if info is None: raise HTTPException(status_code=404, detail="message not found") return info @router.post("/read/refresh") async def refresh_message_read( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, body: RefreshRequest ) -> dict[str, int | None]: info = await read_info(pool, body.account_id, chat_id, message_id) if info is None or not info.can_fetch: return {"job_id": None} job_id = await enqueue( pool, body.account_id, "fetch_read_info", {"chat_id": chat_id, "message_id": message_id}, ) return {"job_id": job_id}