diff --git a/aggregators/kagi-news/src/coves_client.py b/aggregators/kagi-news/src/coves_client.py index 083818b..07b511c 100644 --- a/aggregators/kagi-news/src/coves_client.py +++ b/aggregators/kagi-news/src/coves_client.py @@ -17,7 +17,7 @@ class CovesClient: Handles: - Authentication with aggregator credentials - - Creating posts in communities (social.coves.post.create) + - Creating posts in communities (social.coves.community.post.create) - External embed formatting """ @@ -94,7 +94,7 @@ class CovesClient: self.authenticate() try: - # Prepare post data for social.coves.post.create endpoint + # Prepare post data for social.coves.community.post.create endpoint post_data = { "community": community_handle, "content": content, @@ -114,7 +114,7 @@ class CovesClient: logger.info(f"Creating post in community: {community_handle}") # Make direct HTTP request to XRPC endpoint - url = f"{self.api_url}/xrpc/social.coves.post.create" + url = f"{self.api_url}/xrpc/social.coves.community.post.create" headers = { "Authorization": f"Bearer {self.client._session.access_jwt}", "Content-Type": "application/json" diff --git a/cmd/server/main.go b/cmd/server/main.go index b4bbcea..15e3e21 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -306,7 +306,7 @@ func main() { postJetstreamURL := os.Getenv("POST_JETSTREAM_URL") if postJetstreamURL == "" { // Listen to post record creation events - postJetstreamURL = "ws://localhost:6008/subscribe?wantedCollections=social.coves.post.record" + postJetstreamURL = "ws://localhost:6008/subscribe?wantedCollections=social.coves.community.post" } postEventConsumer := jetstream.NewPostEventConsumer(postRepo, communityRepo, userService) @@ -319,7 +319,7 @@ func main() { }() log.Printf("Started Jetstream post consumer: %s", postJetstreamURL) - log.Println(" - Indexing: social.coves.post.record CREATE operations") + log.Println(" - Indexing: social.coves.community.post CREATE operations") log.Println(" - UPDATE/DELETE indexing deferred until those features are implemented") // Start Jetstream consumer for aggregators diff --git a/docs/COMMUNITY_FEEDS.md b/docs/COMMUNITY_FEEDS.md index a7598d0..145de6f 100644 --- a/docs/COMMUNITY_FEEDS.md +++ b/docs/COMMUNITY_FEEDS.md @@ -182,7 +182,7 @@ type FeedViewPost struct { ```go type PostView struct { - URI string // at://did:plc:abc/social.coves.post.record/123 + URI string // at://did:plc:abc/social.coves.community.post.record/123 CID string // Content ID RKey string // Record key (TID) Author *AuthorView // Author with handle, avatar, reputation @@ -247,7 +247,7 @@ GET /xrpc/social.coves.feed.getCommunity "feed": [ { "post": { - "uri": "at://did:plc:gaming123/social.coves.post.record/abc", + "uri": "at://did:plc:gaming123/social.coves.community.post.record/abc", "cid": "bafyrei...", "author": { "did": "did:plc:alice", @@ -473,7 +473,7 @@ Allow users to create custom algorithms: GET /xrpc/social.coves.feed.getSkeleton?feed=at://alice/feed/best-memes → Returns: [uri1, uri2, uri3, ...] -GET /xrpc/social.coves.post.get?uris=[...] +GET /xrpc/social.coves.community.post.get?uris=[...] → Returns: [full posts] ``` @@ -556,7 +556,7 @@ Show post's position in thread: ## Lexicon Updates -### Updated: `social.coves.post.get` +### Updated: `social.coves.community.post.get` **Changes:** 1. ✅ Batch URIs: `uri` → `uris[]` (max 25) @@ -638,7 +638,7 @@ GET /xrpc/social.coves.feed.getCommunity?community=gaming&sort=hot // Custom feed (power users) GET /xrpc/social.coves.feed.getSkeleton?feed=at://alice/feed/best-memes → Returns URIs -GET /xrpc/social.coves.post.get?uris=[...] +GET /xrpc/social.coves.community.post.get?uris=[...] → Hydrates posts ``` diff --git a/docs/FEED_SYSTEM_IMPLEMENTATION.md b/docs/FEED_SYSTEM_IMPLEMENTATION.md index 83d591b..3e8fdc2 100644 --- a/docs/FEED_SYSTEM_IMPLEMENTATION.md +++ b/docs/FEED_SYSTEM_IMPLEMENTATION.md @@ -222,7 +222,7 @@ curl -X GET \ "feed": [ { "post": { - "uri": "at://did:plc:community-gaming/social.coves.post.record/3k...", + "uri": "at://did:plc:community-gaming/social.coves.community.post.record/3k...", "cid": "bafyrei...", "author": { "did": "did:plc:alice", diff --git a/docs/PRD_GOVERNANCE.md b/docs/PRD_GOVERNANCE.md index 5fc2893..83f38db 100644 --- a/docs/PRD_GOVERNANCE.md +++ b/docs/PRD_GOVERNANCE.md @@ -291,7 +291,7 @@ Content rules are stored in `social.coves.community.profile` under the `contentR - [ ] Go structs: `ContentRules` type in community models - [ ] Repository: Parse and store `contentRules` from community profiles - [ ] Service: `ValidatePostAgainstRules(post, community)` function -- [ ] Handler: Integrate validation into `social.coves.post.create` +- [ ] Handler: Integrate validation into `social.coves.community.post.create` - [ ] AppView indexing: Index post characteristics (embed_type, text_length, etc.) - [ ] Tests: Comprehensive rule validation tests - [ ] Documentation: Content rules guide for community creators diff --git a/docs/PRD_POSTS.md b/docs/PRD_POSTS.md index 4f67e4c..8f8f8b8 100644 --- a/docs/PRD_POSTS.md +++ b/docs/PRD_POSTS.md @@ -45,7 +45,7 @@ Post appears in feeds **Repository Structure:** ``` -Repository: at://did:plc:community789/social.coves.post.record/3k2a4b5c6d7e +Repository: at://did:plc:community789/social.coves.community.post.record/3k2a4b5c6d7e Owner: did:plc:community789 (community owns the post) Author: did:plc:user123 (tracked in record metadata) Hosted By: did:web:coves.social (instance manages community credentials) @@ -77,7 +77,7 @@ Posts are validated against community-specific content rules at creation time. C **Implementation checklist:** - [x] Lexicon: `contentRules` in `social.coves.community.profile` ✅ -- [x] Lexicon: `postType` removed from `social.coves.post.create` ✅ +- [x] Lexicon: `postType` removed from `social.coves.community.post.create` ✅ - [ ] Validation: `ValidatePostAgainstRules()` service function - [ ] Handler: Integrate validation in post creation endpoint - [ ] AppView: Index derived characteristics (embed_type, text_length, etc.) @@ -90,11 +90,11 @@ Posts are validated against community-specific content rules at creation time. C **Priority:** CRITICAL - Posts are the foundation of the platform #### Create Post -- [x] Lexicon: `social.coves.post.record` ✅ -- [x] Lexicon: `social.coves.post.create` ✅ +- [x] Lexicon: `social.coves.community.post.record` ✅ +- [x] Lexicon: `social.coves.community.post.create` ✅ - [x] Removed `postType` enum in favor of content rules ✅ (2025-10-18) - [x] Removed `postType` from record and get lexicons ✅ (2025-10-18) -- [x] **Handler:** `POST /xrpc/social.coves.post.create` ✅ (Alpha - see IMPLEMENTATION_POST_CREATION.md) +- [x] **Handler:** `POST /xrpc/social.coves.community.post.create` ✅ (Alpha - see IMPLEMENTATION_POST_CREATION.md) - ✅ Accept: community (DID/handle), title (optional), content, facets, embed, contentLabels - ✅ Validate: User is authenticated, community exists, content within limits - ✅ Write: Create record in **community's PDS repository** @@ -124,8 +124,8 @@ Posts are validated against community-specific content rules at creation time. C - [x] **E2E Test:** Create text post → Write to **community's PDS** → Index via Jetstream → Verify in AppView ✅ #### Get Post -- [x] Lexicon: `social.coves.post.get` ✅ -- [ ] **Handler:** `GET /xrpc/social.coves.post.get?uri=at://...` +- [x] Lexicon: `social.coves.community.post.get` ✅ +- [ ] **Handler:** `GET /xrpc/social.coves.community.post.get?uri=at://...` - Accept: AT-URI of post - Return: Full post view with author, community, stats, viewer state - [ ] **Service Layer:** `PostService.Get(uri, viewerDID)` @@ -139,8 +139,8 @@ Posts are validated against community-specific content rules at creation time. C - [ ] **E2E Test:** Get post by URI → Verify all fields populated #### Update Post -- [x] Lexicon: `social.coves.post.update` ✅ -- [ ] **Handler:** `POST /xrpc/social.coves.post.update` +- [x] Lexicon: `social.coves.community.post.update` ✅ +- [ ] **Handler:** `POST /xrpc/social.coves.community.post.update` - Accept: uri, title, content, facets, embed, contentLabels, editNote - Validate: User is post author, within 24-hour edit window - Write: Update record in **community's PDS** @@ -157,8 +157,8 @@ Posts are validated against community-specific content rules at creation time. C - [ ] **E2E Test:** Update post → Verify edit reflected in AppView #### Delete Post -- [x] Lexicon: `social.coves.post.delete` ✅ -- [ ] **Handler:** `POST /xrpc/social.coves.post.delete` +- [x] Lexicon: `social.coves.community.post.delete` ✅ +- [ ] **Handler:** `POST /xrpc/social.coves.community.post.delete` - Accept: uri - Validate: User is post author OR community moderator - Write: Delete record from **community's PDS** @@ -251,7 +251,7 @@ Posts are validated against community-specific content rules at creation time. C #### Post Event Handling - [x] **Consumer:** `PostConsumer.HandlePostEvent()` ✅ (2025-10-19) - - ✅ Listen for `social.coves.post.record` CREATE from **community repositories** + - ✅ Listen for `social.coves.community.post.record` CREATE from **community repositories** - ✅ Parse post record, extract author DID and community DID (from AT-URI owner) - ⚠️ **Derive post characteristics:** DEFERRED (embed_type, text_length, has_title, has_embed for content rules filtering) - ✅ Insert in AppView PostgreSQL (CREATE only - UPDATE/DELETE deferred) @@ -447,7 +447,7 @@ CREATE INDEX idx_votes_voter_subject ON votes(voter_did, subject_uri) WHERE dele - [ ] **Tag Storage:** Tags live in **user's repository** (users own their tags) #### Crossposting -- [x] Lexicon: `social.coves.post.crosspost` ✅ +- [x] Lexicon: `social.coves.community.post.crosspost` ✅ - [ ] **Crosspost Tracking:** Share post to multiple communities - [ ] **Implementation:** Create new post record in each community's repository - [ ] **Crosspost Chain:** Track all crosspost relationships @@ -461,7 +461,7 @@ CREATE INDEX idx_votes_voter_subject ON votes(voter_did, subject_uri) WHERE dele - [ ] **AppView Query:** Endpoint to fetch user's saved posts ### Post Search -- [x] Lexicon: `social.coves.post.search` ✅ +- [x] Lexicon: `social.coves.community.post.search` ✅ - [ ] **Search Parameters:** - Query string (q) - Filter by community @@ -583,7 +583,7 @@ CREATE INDEX idx_votes_voter_subject ON votes(voter_did, subject_uri) WHERE dele - **Reuses Token Refresh:** Can leverage existing community credential management **Implementation Details:** -- Post AT-URI: `at://community_did/social.coves.post.record/tid` +- Post AT-URI: `at://community_did/social.coves.community.post.record/tid` - Write operations use community's PDS credentials (encrypted, stored in AppView) - Author tracked in post record's `author` field (DID) - Moderators can delete any post in their community @@ -756,7 +756,7 @@ CREATE INDEX idx_votes_voter_subject ON votes(voter_did, subject_uri) WHERE dele ## Lexicon Summary -### `social.coves.post.record` +### `social.coves.community.post.record` **Status:** ✅ Defined, implementation TODO **Last Updated:** 2025-10-18 (removed `postType` enum) @@ -781,7 +781,7 @@ CREATE INDEX idx_votes_voter_subject ON votes(voter_did, subject_uri) WHERE dele - Post "type" is derived from structure (has embed? what embed type? has title? text length?) - Community's `contentRules` validate post structure at creation time -### `social.coves.post.create` (Procedure) +### `social.coves.community.post.create` (Procedure) **Status:** ✅ Defined, implementation TODO **Last Updated:** 2025-10-18 (removed `postType` parameter) diff --git a/docs/aggregators/PRD_AGGREGATORS.md b/docs/aggregators/PRD_AGGREGATORS.md index 5afa22d..66e80a9 100644 --- a/docs/aggregators/PRD_AGGREGATORS.md +++ b/docs/aggregators/PRD_AGGREGATORS.md @@ -23,7 +23,7 @@ Aggregators follow established atProto patterns for autonomous services (Feed Ge 1. **Aggregators are Actors, Not a Separate System** - Each aggregator has its own DID - Authenticate as themselves via JWT - - Use existing `social.coves.post.create` endpoint + - Use existing `social.coves.community.post.create` endpoint - Post record's `author` field = aggregator DID (server-populated) - No separate posting API needed @@ -89,7 +89,7 @@ Grants an aggregator permission to post with specific configuration. Aggregator Service (External) │ │ 1. Authenticates as aggregator DID (JWT) - │ 2. Calls social.coves.post.create + │ 2. Calls social.coves.community.post.create ▼ Coves AppView Handler │ @@ -120,7 +120,7 @@ Community Feed ### For Aggregators -- **`social.coves.post.create`** - Modified to handle aggregator auth +- **`social.coves.community.post.create`** - Modified to handle aggregator auth - **`social.coves.aggregator.getAuthorizations`** - Query authorized communities ### For Discovery @@ -312,7 +312,7 @@ Potential first aggregator: RSS news bot for select communities. --- -### 2025-10-19: Reuse `social.coves.post.create` Endpoint +### 2025-10-19: Reuse `social.coves.community.post.create` Endpoint **Decision:** Aggregators use existing post creation endpoint. **Rationale:** diff --git a/docs/aggregators/PRD_KAGI_NEWS_RSS.md b/docs/aggregators/PRD_KAGI_NEWS_RSS.md index 0336887..ab4d2f6 100644 --- a/docs/aggregators/PRD_KAGI_NEWS_RSS.md +++ b/docs/aggregators/PRD_KAGI_NEWS_RSS.md @@ -172,14 +172,14 @@ Analysis of live Kagi News feeds confirms the following structure: │ 3. Deduplication: Tracks posted items via JSON state file │ │ 4. Feed Mapper: Maps feed URLs to community handles │ │ 5. Post Formatter: Converts to Coves post format │ -│ 6. Post Publisher: Calls social.coves.post.create via XRPC │ +│ 6. Post Publisher: Calls social.coves.community.post.create via XRPC │ │ 7. Blob Uploader: Handles image upload to ATProto │ └─────────────────────────────────────────────────────────────┘ │ │ Authenticated XRPC calls ▼ ┌─────────────────────────────────────────────────────────────┐ -│ Coves AppView (social.coves.post.create) │ +│ Coves AppView (social.coves.community.post.create) │ │ - Validates aggregator authorization │ │ - Creates post with author = did:plc:[aggregator-did] │ │ - Indexes to community feeds │ @@ -271,7 +271,7 @@ log_level: "info" ```json { - "$type": "social.coves.post.record", + "$type": "social.coves.community.post.record", "author": "did:plc:[aggregator-did]", "community": "world-news.coves.social", "title": "{Kagi story title}", diff --git a/internal/api/handlers/post/create.go b/internal/api/handlers/post/create.go index b93d8d0..06f77fa 100644 --- a/internal/api/handlers/post/create.go +++ b/internal/api/handlers/post/create.go @@ -21,7 +21,7 @@ func NewCreateHandler(service posts.Service) *CreateHandler { } } -// HandleCreate handles POST /xrpc/social.coves.post.create +// HandleCreate handles POST /xrpc/social.coves.community.post.create // Creates a new post in a community's repository func (h *CreateHandler) HandleCreate(w http.ResponseWriter, r *http.Request) { // 1. Check HTTP method diff --git a/internal/api/routes/post.go b/internal/api/routes/post.go index 6c97dac..d2be121 100644 --- a/internal/api/routes/post.go +++ b/internal/api/routes/post.go @@ -9,18 +9,18 @@ import ( ) // RegisterPostRoutes registers post-related XRPC endpoints on the router -// Implements social.coves.post.* lexicon endpoints +// Implements social.coves.community.post.* lexicon endpoints func RegisterPostRoutes(r chi.Router, service posts.Service, authMiddleware *middleware.AtProtoAuthMiddleware) { // Initialize handlers createHandler := post.NewCreateHandler(service) // Procedure endpoints (POST) - require authentication - // social.coves.post.create - create a new post in a community - r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.create", createHandler.HandleCreate) + // social.coves.community.post.create - create a new post in a community + r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.create", createHandler.HandleCreate) // Future endpoints (Beta): - // r.Get("/xrpc/social.coves.post.get", getHandler.HandleGet) - // r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.update", updateHandler.HandleUpdate) - // r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.delete", deleteHandler.HandleDelete) - // r.Get("/xrpc/social.coves.post.list", listHandler.HandleList) + // r.Get("/xrpc/social.coves.community.post.get", getHandler.HandleGet) + // r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.update", updateHandler.HandleUpdate) + // r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.delete", deleteHandler.HandleDelete) + // r.Get("/xrpc/social.coves.community.post.list", listHandler.HandleList) } diff --git a/internal/atproto/lexicon/social/coves/feed/comment.json b/internal/atproto/lexicon/social/coves/feed/comment.json new file mode 100644 index 0000000..a567181 --- /dev/null +++ b/internal/atproto/lexicon/social/coves/feed/comment.json @@ -0,0 +1,80 @@ +{ + "lexicon": 1, + "id": "social.coves.feed.comment", + "defs": { + "main": { + "type": "record", + "description": "A comment on a post or another comment. Comments live in user repositories and support nested threading.", + "key": "tid", + "record": { + "type": "object", + "required": ["reply", "content", "createdAt"], + "properties": { + "reply": { + "type": "ref", + "ref": "#replyRef", + "description": "Reference to the post and parent being replied to" + }, + "content": { + "type": "string", + "maxGraphemes": 3000, + "maxLength": 30000, + "description": "Comment text content" + }, + "facets": { + "type": "array", + "description": "Annotations for rich text (mentions, links, etc.)", + "items": { + "type": "ref", + "ref": "social.coves.richtext.facet" + } + }, + "embed": { + "type": "union", + "description": "Embedded media or quoted posts", + "refs": [ + "social.coves.embed.images", + "social.coves.embed.post" + ] + }, + "langs": { + "type": "array", + "description": "Languages used in the comment content (ISO 639-1)", + "maxLength": 3, + "items": { + "type": "string", + "format": "language" + } + }, + "labels": { + "type": "ref", + "ref": "com.atproto.label.defs#selfLabels", + "description": "Self-applied content labels" + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Timestamp of comment creation" + } + } + } + }, + "replyRef": { + "type": "object", + "description": "References for maintaining thread structure. Root always points to the original post, parent points to the immediate parent (post or comment).", + "required": ["root", "parent"], + "properties": { + "root": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong reference to the original post that started the thread" + }, + "parent": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong reference to the immediate parent (post or comment) being replied to" + } + } + } + } +} diff --git a/internal/atproto/lexicon/social/coves/interaction/comment.json b/internal/atproto/lexicon/social/coves/interaction/comment.json deleted file mode 100644 index 12482ef..0000000 --- a/internal/atproto/lexicon/social/coves/interaction/comment.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.interaction.comment", - "defs": { - "main": { - "type": "record", - "description": "A comment on a post or another comment", - "key": "tid", - "record": { - "type": "object", - "required": ["subject", "content", "createdAt"], - "properties": { - "subject": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of post or comment being replied to" - }, - "content": { - "type": "union", - "refs": ["#textContent", "#imageContent", "#stickerContent"] - }, - "location": { - "type": "ref", - "ref": "social.coves.actor.profile#geoLocation" - }, - "translatedFrom": { - "type": "string", - "maxLength": 10, - "description": "Language code if auto-translated (ISO 639-1)" - }, - "createdAt": { - "type": "string", - "format": "datetime" - } - } - } - }, - "textContent": { - "type": "object", - "required": ["text"], - "properties": { - "text": { - "type": "string", - "maxLength": 10000, - "description": "Comment text" - }, - "facets": { - "type": "array", - "description": "Rich text annotations", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - } - } - }, - "imageContent": { - "type": "object", - "required": ["image"], - "properties": { - "image": { - "type": "ref", - "ref": "social.coves.embed.images#image" - }, - "caption": { - "type": "string", - "maxLength": 1000 - } - } - }, - "stickerContent": { - "type": "object", - "required": ["stickerId"], - "properties": { - "stickerId": { - "type": "string", - "description": "Reference to a sticker in a sticker pack" - }, - "stickerPackId": { - "type": "string", - "description": "Reference to the sticker pack" - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/interaction/createComment.json b/internal/atproto/lexicon/social/coves/interaction/createComment.json deleted file mode 100644 index 42d9187..0000000 --- a/internal/atproto/lexicon/social/coves/interaction/createComment.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.interaction.createComment", - "defs": { - "main": { - "type": "procedure", - "description": "Create a comment on a post or another comment", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["parent", "text"], - "properties": { - "parent": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the post or comment being replied to" - }, - "text": { - "type": "string", - "maxGraphemes": 3000, - "maxLength": 30000, - "description": "Comment text" - }, - "textFacets": { - "type": "array", - "description": "Rich text annotations", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri", "cid"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the created comment" - }, - "cid": { - "type": "string", - "format": "cid", - "description": "CID of the created comment" - } - } - } - }, - "errors": [ - { - "name": "ParentNotFound", - "description": "Parent post or comment not found" - }, - { - "name": "NotAuthorized", - "description": "User is not authorized to comment" - }, - { - "name": "ThreadLocked", - "description": "Comment thread is locked" - }, - { - "name": "Banned", - "description": "User is banned from this community" - } - ] - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/interaction/deleteComment.json b/internal/atproto/lexicon/social/coves/interaction/deleteComment.json deleted file mode 100644 index 34fefd7..0000000 --- a/internal/atproto/lexicon/social/coves/interaction/deleteComment.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.interaction.deleteComment", - "defs": { - "main": { - "type": "procedure", - "description": "Delete a comment", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the comment to delete" - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "properties": {} - } - }, - "errors": [ - { - "name": "CommentNotFound", - "description": "Comment not found" - }, - { - "name": "NotAuthorized", - "description": "User is not authorized to delete this comment" - } - ] - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/create.json b/internal/atproto/lexicon/social/coves/post/create.json deleted file mode 100644 index cdd236b..0000000 --- a/internal/atproto/lexicon/social/coves/post/create.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.create", - "defs": { - "main": { - "type": "procedure", - "description": "Create a new post in a community", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["community"], - "properties": { - "community": { - "type": "string", - "format": "at-identifier", - "description": "DID or handle of the community to post in" - }, - "title": { - "type": "string", - "maxGraphemes": 300, - "maxLength": 3000, - "description": "Post title (optional for microblog, image, and video posts)" - }, - "content": { - "type": "string", - "maxLength": 50000, - "description": "Post content - main text for text posts, description for media, etc." - }, - "facets": { - "type": "array", - "description": "Rich text annotations for content", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - }, - "embed": { - "type": "union", - "description": "Embedded content - images, videos, external links, or quoted posts", - "refs": [ - "social.coves.embed.images", - "social.coves.embed.video", - "social.coves.embed.external", - "social.coves.embed.post" - ] - }, - "originalAuthor": { - "type": "ref", - "ref": "social.coves.post.record#originalAuthor", - "description": "For microblog posts - information about the original author" - }, - "federatedFrom": { - "type": "ref", - "ref": "social.coves.federation.post", - "description": "Reference to original federated post (for microblog posts)" - }, - "contentLabels": { - "type": "array", - "description": "Self-applied content labels", - "items": { - "type": "string", - "knownValues": ["nsfw", "spoiler", "violence"], - "maxLength": 32 - } - }, - "location": { - "type": "ref", - "ref": "social.coves.actor.profile#geoLocation", - "description": "Geographic location where post was created" - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri", "cid"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the created post" - }, - "cid": { - "type": "string", - "format": "cid", - "description": "CID of the created post" - } - } - } - }, - "errors": [ - { - "name": "CommunityNotFound", - "description": "Community not found" - }, - { - "name": "NotAuthorized", - "description": "User is not authorized to post in this community" - }, - { - "name": "Banned", - "description": "User is banned from this community" - }, - { - "name": "InvalidContent", - "description": "Post content violates community rules" - }, - { - "name": "ContentRuleViolation", - "description": "Post violates community content rules (e.g., embeds not allowed, text too short)" - } - ] - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/crosspost.json b/internal/atproto/lexicon/social/coves/post/crosspost.json deleted file mode 100644 index 5f338ab..0000000 --- a/internal/atproto/lexicon/social/coves/post/crosspost.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.crosspost", - "defs": { - "main": { - "type": "record", - "description": "A record tracking crosspost relationships between posts", - "key": "tid", - "record": { - "type": "object", - "required": ["originalPost", "crosspostOf", "createdAt"], - "properties": { - "originalPost": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the original post in the crosspost chain" - }, - "crosspostOf": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the immediate parent this is a crosspost of" - }, - "allCrossposts": { - "type": "array", - "description": "Array of AT-URIs of all posts in the crosspost chain", - "items": { - "type": "string", - "format": "at-uri" - } - }, - "createdAt": { - "type": "string", - "format": "datetime" - } - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/delete.json b/internal/atproto/lexicon/social/coves/post/delete.json deleted file mode 100644 index 8afd6db..0000000 --- a/internal/atproto/lexicon/social/coves/post/delete.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.delete", - "defs": { - "main": { - "type": "procedure", - "description": "Delete a post", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the post to delete" - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "properties": {} - } - }, - "errors": [ - { - "name": "PostNotFound", - "description": "Post not found" - }, - { - "name": "NotAuthorized", - "description": "User is not authorized to delete this post" - } - ] - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/get.json b/internal/atproto/lexicon/social/coves/post/get.json deleted file mode 100644 index e83d724..0000000 --- a/internal/atproto/lexicon/social/coves/post/get.json +++ /dev/null @@ -1,294 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.get", - "defs": { - "main": { - "type": "query", - "description": "Get posts by AT-URI. Supports batch fetching for feed hydration. Returns posts in same order as input URIs.", - "parameters": { - "type": "params", - "required": ["uris"], - "properties": { - "uris": { - "type": "array", - "description": "List of post AT-URIs to fetch (max 25)", - "items": { - "type": "string", - "format": "at-uri" - }, - "maxLength": 25, - "minLength": 1 - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["posts"], - "properties": { - "posts": { - "type": "array", - "description": "Array of post views. May include notFound/blocked entries for missing posts.", - "items": { - "type": "union", - "refs": ["#postView", "#notFoundPost", "#blockedPost"] - } - } - } - } - }, - "errors": [ - {"name": "InvalidRequest", "description": "Invalid URI format or empty array"} - ] - }, - "postView": { - "type": "object", - "required": ["uri", "cid", "author", "record", "community", "createdAt", "indexedAt"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "cid": { - "type": "string", - "format": "cid" - }, - "author": { - "type": "ref", - "ref": "#authorView" - }, - "record": { - "type": "unknown", - "description": "The actual post record (text, image, video, etc.)" - }, - "community": { - "type": "ref", - "ref": "#communityRef" - }, - "title": { - "type": "string" - }, - "text": { - "type": "string" - }, - "textFacets": { - "type": "array", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - }, - "embed": { - "type": "union", - "description": "Embedded content (images, video, link preview, or quoted post)", - "refs": [ - "social.coves.embed.images#view", - "social.coves.embed.video#view", - "social.coves.embed.external#view", - "social.coves.embed.record#view", - "social.coves.embed.recordWithMedia#view" - ] - }, - "language": { - "type": "string", - "format": "language" - }, - "createdAt": { - "type": "string", - "format": "datetime" - }, - "editedAt": { - "type": "string", - "format": "datetime" - }, - "indexedAt": { - "type": "string", - "format": "datetime", - "description": "When this post was indexed by the AppView" - }, - "stats": { - "type": "ref", - "ref": "#postStats" - }, - "viewer": { - "type": "ref", - "ref": "#viewerState" - } - } - }, - "authorView": { - "type": "object", - "required": ["did", "handle"], - "properties": { - "did": { - "type": "string", - "format": "did" - }, - "handle": { - "type": "string", - "format": "handle" - }, - "displayName": { - "type": "string" - }, - "avatar": { - "type": "string", - "format": "uri" - }, - "reputation": { - "type": "integer", - "description": "Author's reputation in the community" - } - } - }, - "communityRef": { - "type": "object", - "required": ["did", "name"], - "properties": { - "did": { - "type": "string", - "format": "did" - }, - "name": { - "type": "string" - }, - "avatar": { - "type": "string", - "format": "uri" - } - } - }, - "notFoundPost": { - "type": "object", - "description": "Post was not found (deleted, never indexed, or invalid URI)", - "required": ["uri", "notFound"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "notFound": { - "type": "boolean", - "const": true - } - } - }, - "blockedPost": { - "type": "object", - "description": "Post is blocked due to viewer blocking author/community, or community moderation", - "required": ["uri", "blocked"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "blocked": { - "type": "boolean", - "const": true - }, - "blockedBy": { - "type": "string", - "enum": ["author", "community", "moderator"], - "description": "What caused the block: viewer blocked author, viewer blocked community, or post was removed by moderators" - }, - "author": { - "type": "ref", - "ref": "#blockedAuthor" - }, - "community": { - "type": "ref", - "ref": "#blockedCommunity" - } - } - }, - "blockedAuthor": { - "type": "object", - "description": "Minimal author info for blocked posts", - "required": ["did"], - "properties": { - "did": { - "type": "string", - "format": "did" - } - } - }, - "blockedCommunity": { - "type": "object", - "description": "Minimal community info for blocked posts", - "required": ["did"], - "properties": { - "did": { - "type": "string", - "format": "did" - }, - "name": { - "type": "string" - } - } - }, - "postStats": { - "type": "object", - "required": ["upvotes", "downvotes", "score", "commentCount"], - "properties": { - "upvotes": { - "type": "integer", - "minimum": 0 - }, - "downvotes": { - "type": "integer", - "minimum": 0 - }, - "score": { - "type": "integer", - "description": "Calculated score (upvotes - downvotes)" - }, - "commentCount": { - "type": "integer", - "minimum": 0 - }, - "shareCount": { - "type": "integer", - "minimum": 0 - }, - "tagCounts": { - "type": "object", - "description": "Aggregate counts of tags applied by community members", - "additionalProperties": { - "type": "integer", - "minimum": 0 - } - } - } - }, - "viewerState": { - "type": "object", - "properties": { - "vote": { - "type": "string", - "enum": ["up", "down"], - "description": "Viewer's vote on this post" - }, - "voteUri": { - "type": "string", - "format": "at-uri" - }, - "saved": { - "type": "boolean" - }, - "savedUri": { - "type": "string", - "format": "at-uri" - }, - "tags": { - "type": "array", - "description": "Tags applied by the viewer to this post", - "items": { - "type": "string", - "maxLength": 32 - } - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/getCrosspostChain.json b/internal/atproto/lexicon/social/coves/post/getCrosspostChain.json deleted file mode 100644 index 03552c9..0000000 --- a/internal/atproto/lexicon/social/coves/post/getCrosspostChain.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.getCrosspostChain", - "defs": { - "main": { - "type": "procedure", - "description": "Get all crossposts in a crosspost chain for a given post", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of any post in the crosspost chain" - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["crossposts"], - "properties": { - "crossposts": { - "type": "array", - "description": "All posts in the crosspost chain", - "items": { - "type": "ref", - "ref": "#crosspostView" - } - } - } - } - } - }, - "crosspostView": { - "type": "object", - "required": ["uri", "community", "author", "createdAt"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the post" - }, - "community": { - "type": "object", - "required": ["uri", "name"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the community" - }, - "name": { - "type": "string", - "description": "Display name of the community" - }, - "handle": { - "type": "string", - "description": "Handle of the community" - } - } - }, - "author": { - "type": "object", - "required": ["did", "handle"], - "properties": { - "did": { - "type": "string", - "format": "did" - }, - "handle": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "avatar": { - "type": "string", - "format": "uri" - } - } - }, - "isOriginal": { - "type": "boolean", - "description": "Whether this is the original post in the chain" - }, - "createdAt": { - "type": "string", - "format": "datetime" - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/record.json b/internal/atproto/lexicon/social/coves/post/record.json deleted file mode 100644 index 3a09db0..0000000 --- a/internal/atproto/lexicon/social/coves/post/record.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.record", - "defs": { - "main": { - "type": "record", - "description": "A unified post record supporting multiple content types", - "key": "tid", - "record": { - "type": "object", - "required": ["$type", "community", "author", "createdAt"], - "properties": { - "$type": { - "type": "string", - "const": "social.coves.post.record", - "description": "The record type identifier" - }, - "community": { - "type": "string", - "format": "at-identifier", - "description": "DID or handle of the community this was posted to" - }, - "author": { - "type": "string", - "format": "did", - "description": "DID of the user who created this post. Server-populated from authenticated session; clients MUST NOT provide this field. Required for attribution, moderation, and accountability." - }, - "title": { - "type": "string", - "maxGraphemes": 300, - "maxLength": 3000, - "description": "Post title (optional for microblog, image, and video posts)" - }, - "content": { - "type": "string", - "maxLength": 50000, - "description": "Post content - main text for text posts, description for media, etc." - }, - "facets": { - "type": "array", - "description": "Rich text annotations for content", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - }, - "embed": { - "type": "union", - "description": "Embedded content - images, videos, external links, or quoted posts", - "refs": [ - "social.coves.embed.images", - "social.coves.embed.video", - "social.coves.embed.external", - "social.coves.embed.post" - ] - }, - "originalAuthor": { - "type": "ref", - "ref": "#originalAuthor", - "description": "For microblog posts - information about the original author from federated platform" - }, - "contentLabels": { - "type": "array", - "description": "Self-applied content labels", - "items": { - "type": "string", - "knownValues": ["nsfw", "spoiler", "violence"], - "maxLength": 32 - } - }, - "federatedFrom": { - "type": "ref", - "ref": "social.coves.federation.post", - "description": "Reference to original federated post (if applicable)" - }, - "location": { - "type": "ref", - "ref": "social.coves.actor.profile#geoLocation", - "description": "Geographic location where post was created" - }, - "crosspostOf": { - "type": "string", - "format": "at-uri", - "description": "If this is a crosspost, AT-URI of the post this is a crosspost of" - }, - "crosspostChain": { - "type": "array", - "description": "Array of AT-URIs of all posts in the crosspost chain (including this one)", - "items": { - "type": "string", - "format": "at-uri" - } - }, - "createdAt": { - "type": "string", - "format": "datetime" - } - } - } - }, - "originalAuthor": { - "type": "object", - "description": "Information about the original author from a federated platform", - "required": ["handle"], - "properties": { - "did": { - "type": "string", - "format": "did", - "description": "Original author's DID (if available)" - }, - "handle": { - "type": "string", - "maxLength": 253, - "description": "Original author's handle" - }, - "displayName": { - "type": "string", - "maxLength": 640, - "description": "Original author's display name" - }, - "avatar": { - "type": "string", - "format": "uri", - "description": "URL to original author's avatar" - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/search.json b/internal/atproto/lexicon/social/coves/post/search.json deleted file mode 100644 index 1a83b75..0000000 --- a/internal/atproto/lexicon/social/coves/post/search.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.search", - "defs": { - "main": { - "type": "query", - "description": "Search for posts", - "parameters": { - "type": "params", - "required": ["q"], - "properties": { - "q": { - "type": "string", - "description": "Search query" - }, - "community": { - "type": "string", - "format": "at-identifier", - "description": "Filter by specific community" - }, - "author": { - "type": "string", - "format": "at-identifier", - "description": "Filter by author" - }, - "type": { - "type": "string", - "enum": ["text", "image", "video", "article", "microblog"], - "description": "Filter by post type" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Filter by tags" - }, - "sort": { - "type": "string", - "enum": ["relevance", "new", "top"], - "default": "relevance" - }, - "timeframe": { - "type": "string", - "enum": ["hour", "day", "week", "month", "year", "all"], - "default": "all" - }, - "limit": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 50 - }, - "cursor": { - "type": "string" - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["posts"], - "properties": { - "posts": { - "type": "array", - "items": { - "type": "ref", - "ref": "social.coves.post.getFeed#feedPost" - } - }, - "cursor": { - "type": "string" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/internal/atproto/lexicon/social/coves/post/update.json b/internal/atproto/lexicon/social/coves/post/update.json deleted file mode 100644 index 3a9e12a..0000000 --- a/internal/atproto/lexicon/social/coves/post/update.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "lexicon": 1, - "id": "social.coves.post.update", - "defs": { - "main": { - "type": "procedure", - "description": "Update an existing post", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the post to update" - }, - "title": { - "type": "string", - "maxGraphemes": 300, - "maxLength": 3000, - "description": "Updated title" - }, - "content": { - "type": "string", - "maxLength": 50000, - "description": "Updated content - main text for text posts, description for media, etc." - }, - "facets": { - "type": "array", - "description": "Updated rich text annotations for content", - "items": { - "type": "ref", - "ref": "social.coves.richtext.facet" - } - }, - "embed": { - "type": "union", - "description": "Updated embedded content (note: changing embed type may be restricted)", - "refs": [ - "social.coves.embed.images", - "social.coves.embed.video", - "social.coves.embed.external", - "social.coves.embed.post" - ] - }, - "contentLabels": { - "type": "array", - "description": "Updated content labels", - "items": { - "type": "string", - "knownValues": ["nsfw", "spoiler", "violence"], - "maxLength": 32 - } - }, - "editNote": { - "type": "string", - "maxLength": 300, - "description": "Optional note explaining the edit" - } - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["uri", "cid"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the updated post" - }, - "cid": { - "type": "string", - "format": "cid", - "description": "New CID of the updated post" - } - } - } - }, - "errors": [ - { - "name": "PostNotFound", - "description": "Post not found" - }, - { - "name": "NotAuthorized", - "description": "User is not authorized to edit this post" - }, - { - "name": "EditWindowExpired", - "description": "Edit window has expired (posts can only be edited within 24 hours)" - }, - { - "name": "InvalidUpdate", - "description": "Invalid update operation (e.g., changing post type)" - } - ] - } - } -} \ No newline at end of file