Skip to main content
Glama
hmumixaM

USCardForum MCP Server

by hmumixaM
topics.py7.33 kB
"""Topics API module for fetching topics and posts.""" from __future__ import annotations from typing import Any from uscardforum.api.base import BaseAPI from uscardforum.models.topics import Post, TopicInfo, TopicSummary class TopicsAPI(BaseAPI): """API for topic and post operations. Handles: - Fetching topic lists (hot, new, top) - Reading topic metadata - Fetching posts from topics """ # ------------------------------------------------------------------------- # Topic Lists # ------------------------------------------------------------------------- def get_hot_topics(self, *, page: int | None = None) -> list[TopicSummary]: """Fetch currently trending topics. Args: page: Page number for pagination (0-indexed, default: 0) Returns: List of hot topic summaries """ params: dict[str, Any] = {} if page is not None: params["page"] = int(page) payload = self._get( "/hot.json", params=params or None, headers={"Accept": "application/json, text/plain, */*"}, ) topics = payload.get("topic_list", {}).get("topics", []) return [TopicSummary(**t) for t in topics] def get_new_topics(self, *, page: int | None = None) -> list[TopicSummary]: """Fetch latest new topics. Args: page: Page number for pagination (0-indexed, default: 0) Returns: List of new topic summaries """ params: dict[str, Any] = {} if page is not None: params["page"] = int(page) payload = self._get( "/latest.json", params=params or None, headers={"Accept": "application/json, text/plain, */*"}, ) topics = payload.get("topic_list", {}).get("topics", []) return [TopicSummary(**t) for t in topics] def get_top_topics( self, period: str = "monthly", *, page: int | None = None ) -> list[TopicSummary]: """Fetch top topics for a time period. Args: period: One of 'daily', 'weekly', 'monthly', 'quarterly', 'yearly' page: Page number for pagination (0-indexed, default: 0) Returns: List of top topic summaries """ allowed = {"daily", "weekly", "monthly", "quarterly", "yearly"} if period not in allowed: raise ValueError(f"period must be one of {sorted(list(allowed))}") params: dict[str, Any] = {"period": period} if page is not None: params["page"] = int(page) payload = self._get( "/top.json", params=params, headers={"Accept": "application/json, text/plain, */*"}, ) topics = payload.get("topic_list", {}).get("topics", []) return [TopicSummary(**t) for t in topics] # ------------------------------------------------------------------------- # Topic Details # ------------------------------------------------------------------------- def get_topic_info(self, topic_id: int) -> TopicInfo: """Fetch topic metadata. Args: topic_id: Topic ID Returns: Topic info with post count, title, timestamps """ payload = self._get(f"/t/{int(topic_id)}.json") return TopicInfo( topic_id=topic_id, title=payload.get("title"), post_count=payload.get("posts_count", 0), highest_post_number=payload.get("highest_post_number", 0), last_posted_at=payload.get("last_posted_at"), ) # ------------------------------------------------------------------------- # Posts # ------------------------------------------------------------------------- def get_topic_posts( self, topic_id: int, *, post_number: int = 1, include_raw: bool = False, ) -> list[Post]: """Fetch a batch of posts starting at a specific post number. Args: topic_id: Topic ID post_number: Starting post number (default: 1) include_raw: Include raw markdown (default: False) Returns: List of posts sorted by post_number """ params_list: list[tuple[str, Any]] = [ ("post_number", int(post_number)), ("asc", "true"), ("include_suggested", "false"), ("include_raw", str(include_raw).lower()), ] payload = self._get(f"t/topic/{int(topic_id)}.json", params=params_list) raw_posts = payload.get("post_stream", {}).get("posts", []) posts = [] for p in raw_posts: post = Post( id=p.get("id", 0), post_number=p.get("post_number", 0), username=p.get("username", ""), cooked=p.get("cooked"), raw=p.get("raw") if include_raw else None, created_at=p.get("created_at"), updated_at=p.get("updated_at"), like_count=p.get("like_count", 0), reply_count=p.get("reply_count", 0), reply_to_post_number=p.get("reply_to_post_number"), ) posts.append(post) posts.sort(key=lambda p: p.post_number) return posts def get_all_topic_posts( self, topic_id: int, *, include_raw: bool = False, start_post_number: int = 1, end_post_number: int | None = None, max_posts: int | None = None, ) -> list[Post]: """Fetch all posts in a topic with automatic pagination. Args: topic_id: Topic ID include_raw: Include raw markdown (default: False) start_post_number: Starting post number (default: 1) end_post_number: Optional ending post number max_posts: Optional maximum posts to fetch Returns: List of all matching posts """ current = max(1, int(start_post_number)) collected: list[Post] = [] seen_numbers: set[int] = set() while True: if max_posts is not None and len(collected) >= int(max_posts): break batch = self.get_topic_posts( topic_id, post_number=current, include_raw=include_raw ) if not batch: break last_in_batch: int | None = None for post in batch: pn = post.post_number if pn not in seen_numbers: if end_post_number is not None and pn > int(end_post_number): last_in_batch = last_in_batch or pn continue seen_numbers.add(pn) collected.append(post) last_in_batch = pn if max_posts is not None and len(collected) >= int(max_posts): break if last_in_batch is None: break current = last_in_batch + 1 if end_post_number is not None and current > int(end_post_number): break collected.sort(key=lambda p: p.post_number) return collected

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hmumixaM/uscardforum-mcp4'

If you have feedback or need assistance with the MCP directory API, please join our Discord server