From edaada0537861c7677d15dd29bafb867e6cdb81c Mon Sep 17 00:00:00 2001 From: Bretton Date: Wed, 5 Nov 2025 15:44:17 -0800 Subject: [PATCH] feat(lexicon): add comment query API lexicon definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add lexicon definitions for comment query API following Bluesky patterns: - social.coves.community.comment.defs: Shared view definitions - commentView: Base view for single comment with stats and viewer state - threadViewComment: Recursive wrapper for threaded replies - Supporting types: commentStats, commentViewerState, commentRef, etc. - social.coves.community.comment.getComments: Query endpoint - Parameters: post (required), sort, depth, limit, cursor, timeframe - Returns threaded comments with nested replies up to depth limit - Supports hot/top/new sorting with Lemmy-style hot ranking Follows atproto best practices: - Composition pattern (threadView wraps baseView) - Union types for error states (notFound, blocked) - Open unions for future extensibility - Strong references with CID version pinning 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../social/coves/community/comment/defs.json | 221 ++++++++++++++++++ .../coves/community/comment/getComments.json | 86 +++++++ 2 files changed, 307 insertions(+) create mode 100644 internal/atproto/lexicon/social/coves/community/comment/defs.json create mode 100644 internal/atproto/lexicon/social/coves/community/comment/getComments.json diff --git a/internal/atproto/lexicon/social/coves/community/comment/defs.json b/internal/atproto/lexicon/social/coves/community/comment/defs.json new file mode 100644 index 0000000..a0e4be7 --- /dev/null +++ b/internal/atproto/lexicon/social/coves/community/comment/defs.json @@ -0,0 +1,221 @@ +{ + "lexicon": 1, + "id": "social.coves.community.comment.defs", + "defs": { + "commentView": { + "type": "object", + "description": "Base view for a single comment with voting, stats, and viewer state", + "required": ["uri", "cid", "author", "record", "post", "content", "createdAt", "indexedAt", "stats"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the comment record" + }, + "cid": { + "type": "string", + "format": "cid", + "description": "CID of the comment record" + }, + "author": { + "type": "ref", + "ref": "social.coves.community.post.get#authorView", + "description": "Comment author information" + }, + "record": { + "type": "unknown", + "description": "The actual comment record verbatim" + }, + "post": { + "type": "ref", + "ref": "#postRef", + "description": "Reference to the parent post" + }, + "parent": { + "type": "ref", + "ref": "#commentRef", + "description": "Reference to parent comment if this is a nested reply" + }, + "content": { + "type": "string", + "description": "Comment text content" + }, + "contentFacets": { + "type": "array", + "description": "Rich text annotations for mentions, links, formatting", + "items": { + "type": "ref", + "ref": "social.coves.richtext.facet" + } + }, + "embed": { + "type": "union", + "description": "Embedded content in the comment (images or quoted post)", + "refs": [ + "social.coves.embed.images#view", + "social.coves.embed.post#view" + ] + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "When the comment was created" + }, + "indexedAt": { + "type": "string", + "format": "datetime", + "description": "When this comment was indexed by the AppView" + }, + "stats": { + "type": "ref", + "ref": "#commentStats", + "description": "Comment statistics (votes, replies)" + }, + "viewer": { + "type": "ref", + "ref": "#commentViewerState", + "description": "Viewer-specific state (vote, saved, etc.)" + } + } + }, + "threadViewComment": { + "type": "object", + "description": "Wrapper for threaded comment structure, similar to Bluesky's threadViewPost pattern", + "required": ["comment"], + "properties": { + "comment": { + "type": "ref", + "ref": "#commentView", + "description": "The comment itself" + }, + "replies": { + "type": "array", + "description": "Nested replies to this comment", + "items": { + "type": "union", + "refs": ["#threadViewComment", "#notFoundComment", "#blockedComment"] + } + }, + "hasMore": { + "type": "boolean", + "description": "True if more replies exist but are not included in this response" + } + } + }, + "commentRef": { + "type": "object", + "description": "Reference to a comment record", + "required": ["uri", "cid"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the comment" + }, + "cid": { + "type": "string", + "format": "cid", + "description": "CID of the comment record" + } + } + }, + "postRef": { + "type": "object", + "description": "Reference to a post record", + "required": ["uri", "cid"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the post" + }, + "cid": { + "type": "string", + "format": "cid", + "description": "CID of the post record" + } + } + }, + "notFoundComment": { + "type": "object", + "description": "Comment was not found (deleted, never indexed, or invalid URI)", + "required": ["uri", "notFound"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the missing comment" + }, + "notFound": { + "type": "boolean", + "const": true, + "description": "Always true for not found comments" + } + } + }, + "blockedComment": { + "type": "object", + "description": "Comment is blocked due to viewer blocking author or moderation action", + "required": ["uri", "blocked"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the blocked comment" + }, + "blocked": { + "type": "boolean", + "const": true, + "description": "Always true for blocked comments" + }, + "blockedBy": { + "type": "string", + "knownValues": ["author", "moderator"], + "description": "What caused the block: viewer blocked author, or comment was removed by moderators" + } + } + }, + "commentStats": { + "type": "object", + "description": "Statistics for a comment", + "required": ["upvotes", "downvotes", "score", "replyCount"], + "properties": { + "upvotes": { + "type": "integer", + "minimum": 0, + "description": "Number of upvotes" + }, + "downvotes": { + "type": "integer", + "minimum": 0, + "description": "Number of downvotes" + }, + "score": { + "type": "integer", + "description": "Calculated score (upvotes - downvotes)" + }, + "replyCount": { + "type": "integer", + "minimum": 0, + "description": "Number of direct replies to this comment" + } + } + }, + "commentViewerState": { + "type": "object", + "description": "Viewer-specific state for a comment", + "properties": { + "vote": { + "type": "string", + "knownValues": ["up", "down"], + "description": "Viewer's vote on this comment" + }, + "voteUri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the viewer's vote record" + } + } + } + } +} diff --git a/internal/atproto/lexicon/social/coves/community/comment/getComments.json b/internal/atproto/lexicon/social/coves/community/comment/getComments.json new file mode 100644 index 0000000..e94db67 --- /dev/null +++ b/internal/atproto/lexicon/social/coves/community/comment/getComments.json @@ -0,0 +1,86 @@ +{ + "lexicon": 1, + "id": "social.coves.community.comment.getComments", + "defs": { + "main": { + "type": "query", + "description": "Get comments for a post with threading and sorting support. Supports hot/top/new sorting, configurable nesting depth, and pagination.", + "parameters": { + "type": "params", + "required": ["post"], + "properties": { + "post": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the post to get comments for" + }, + "sort": { + "type": "string", + "default": "hot", + "knownValues": ["hot", "top", "new"], + "description": "Sort order: hot (trending), top (highest score), new (most recent)" + }, + "timeframe": { + "type": "string", + "knownValues": ["hour", "day", "week", "month", "year", "all"], + "description": "Timeframe for 'top' sort. Ignored for other sort types." + }, + "depth": { + "type": "integer", + "default": 10, + "minimum": 0, + "maximum": 100, + "description": "Maximum reply nesting depth to return. 0 returns only top-level comments." + }, + "limit": { + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 100, + "description": "Maximum number of top-level comments to return per page" + }, + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["comments", "post"], + "properties": { + "comments": { + "type": "array", + "description": "Top-level comments with nested replies up to requested depth", + "items": { + "type": "ref", + "ref": "social.coves.community.comment.defs#threadViewComment" + } + }, + "post": { + "type": "ref", + "ref": "social.coves.community.post.get#postView", + "description": "The post these comments belong to" + }, + "cursor": { + "type": "string", + "description": "Pagination cursor for fetching next page of top-level comments" + } + } + } + }, + "errors": [ + { + "name": "NotFound", + "description": "Post not found" + }, + { + "name": "InvalidRequest", + "description": "Invalid parameters (malformed URI, invalid sort/timeframe combination, etc.)" + } + ] + } + } +} -- 2.51.2