# retoor from __future__ import annotations from typing import Any from devplacepy_services.base.compound import ( AttachmentsIn, AwardsBundleIn, BookmarksIn, CacheBumpIn, CommentCountsIn, CommentsIn, FeedPageIn, FollowBundleIn, LeaderboardBundleIn, NotificationPrefsIn, OnlineUsersIn, PollsIn, ReactionsIn, RecentCommentsIn, RelationsIn, SeoMetaIn, SiteSidebarIn, UserMediaIn, UsersByUidsIn, VoteCountsIn, ) from devplacepy_services.base.errors import http_error from devplacepy_services.base.http import internal_request class BrokerClient: def __init__(self, name: str, url: str | None = None) -> None: self.name = name if url is not None: self.url = url.rstrip("/") else: from devplacepy_services.base.config import broker_url self.url = broker_url(name).rstrip("/") async def post(self, path: str, body: dict[str, Any] | None = None) -> Any: target = f"{self.url}/{path.lstrip('/')}" response = await internal_request("POST", target, json=body or {}) if response.status_code >= 400: payload = response.json() if response.content else {} message = payload.get("error", "Broker request failed") code = payload.get("code", "broker_error") raise http_error(response.status_code, message, code) if not response.content: return None return response.json() async def get(self, path: str, *, params: dict[str, Any] | None = None) -> Any: target = f"{self.url}/{path.lstrip('/')}" response = await internal_request("GET", target, params=params) if response.status_code >= 400: payload = response.json() if response.content else {} message = payload.get("error", "Broker request failed") code = payload.get("code", "broker_error") raise http_error(response.status_code, message, code) if not response.content: return None return response.json() main = BrokerClient("main") devii_tasks = BrokerClient("devii_tasks") devii_lessons = BrokerClient("devii_lessons") async def get_setting(key: str, default: str = "") -> str: payload = await main.get(f"settings/{key}", params={"default": default}) return payload.get("value", default) if isinstance(payload, dict) else default async def get_int_setting(key: str, default: int) -> int: raw = await get_setting(key, str(default)) try: return int(raw) except (TypeError, ValueError): return default async def bump_cache_version(name: str) -> None: await main.post("/cache/bump", CacheBumpIn(name=name).model_dump()) async def get_users_by_uids(uids: list[str]) -> dict: return await main.post("/compound/users-by-uids", UsersByUidsIn(uids=uids).model_dump()) async def get_comment_counts_by_post_uids(post_uids: list[str]) -> dict: return await main.post( "/compound/comment-counts", CommentCountsIn(post_uids=post_uids).model_dump() ) async def get_vote_counts(target_uids: list[str]) -> tuple[dict, dict]: payload = await main.post( "/compound/vote-counts", VoteCountsIn(target_uids=target_uids).model_dump() ) return payload.get("ups", {}), payload.get("downs", {}) async def get_reactions_by_targets( target_type: str, target_uids: list[str], user: dict | None = None ) -> dict: return await main.post( "/compound/reactions", ReactionsIn(target_type=target_type, target_uids=target_uids, user=user).model_dump(), ) async def get_user_bookmarks( user_uid: str, target_type: str, target_uids: list[str] ) -> set[str]: payload = await main.post( "/compound/bookmarks", BookmarksIn( user_uid=user_uid, target_type=target_type, target_uids=target_uids ).model_dump(), ) return set(payload or []) async def get_polls_by_post_uids(post_uids: list[str], user: dict | None = None) -> dict: return await main.post( "/compound/polls", PollsIn(post_uids=post_uids, user=user).model_dump() ) async def get_recent_comments_by_post_uids( post_uids: list[str], limit: int = 3, user: dict | None = None ) -> dict: return await main.post( "/compound/recent-comments", RecentCommentsIn(post_uids=post_uids, limit=limit, user=user).model_dump(), ) async def load_comments( target_type: str, target_uid: str, user: dict | None = None ) -> list: return await main.post( "/compound/comments", CommentsIn(target_type=target_type, target_uid=target_uid, user=user).model_dump(), ) async def get_follow_bundle( user_uid: str, target_uids: list[str] | None = None ) -> dict: return await main.post( "/compound/follow-bundle", FollowBundleIn(user_uid=user_uid, target_uids=target_uids).model_dump(), ) async def get_user_relations_bundle(viewer_uid: str | None = None) -> dict: return await main.post( "/compound/relations", RelationsIn(viewer_uid=viewer_uid).model_dump() ) async def get_online_users(cutoff_iso: str, limit: int = 30) -> list: return await main.post( "/compound/online-users", OnlineUsersIn(cutoff_iso=cutoff_iso, limit=limit).model_dump(), ) async def get_notification_prefs(user_uid: str) -> list: return await main.post( "/compound/notification-prefs", NotificationPrefsIn(user_uid=user_uid).model_dump(), ) async def get_leaderboard_bundle( limit: int = 50, offset: int = 0, viewer_uid: str | None = None ) -> dict: return await main.post( "/compound/leaderboard", LeaderboardBundleIn(limit=limit, offset=offset, viewer_uid=viewer_uid).model_dump(), ) async def get_site_sidebar(authors_limit: int = 5) -> dict: return await main.post( "/compound/site-sidebar", SiteSidebarIn(authors_limit=authors_limit).model_dump() ) async def get_awards_bundle( receiver_uid: str, page: int = 1, per_page: int = 12, profile_user: dict | None = None, ) -> dict: return await main.post( "/compound/awards", AwardsBundleIn( receiver_uid=receiver_uid, page=page, per_page=per_page, profile_user=profile_user, ).model_dump(), ) async def get_attachments_batch(resource_type: str, resource_uids: list[str]) -> dict: return await main.post( "/compound/attachments", AttachmentsIn(resource_type=resource_type, resource_uids=resource_uids).model_dump(), ) async def get_user_media( user_uid: str, page: int = 1, per_page: int = 24 ) -> dict: return await main.post( "/compound/user-media", UserMediaIn(user_uid=user_uid, page=page, per_page=per_page).model_dump(), ) async def get_seo_metadata_batch(target_type: str, uids: list[str]) -> dict: return await main.post( "/compound/seo-meta", SeoMetaIn(target_type=target_type, uids=uids).model_dump() ) async def build_feed_page(**kwargs) -> dict: return await main.post("/compound/feed-page", FeedPageIn(**kwargs).model_dump())