From c53a8b9b40fb5ee686698108bc946b3e75ed70a7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Sugi Date: Wed, 17 Apr 2024 11:03:19 +0900 Subject: [PATCH] feat: Update API, based on the latest lexicon schemas (#157) https://github.com/bluesky-social/atproto/commit/45981de5c1639dc16282863c90d023117352d81a --- atrium-api/src/app/bsky/actor/defs.rs | 4 ++ atrium-api/src/app/bsky/embed/record.rs | 6 ++ atrium-api/src/app/bsky/feed.rs | 1 + atrium-api/src/app/bsky/feed/defs.rs | 43 +++++++++++++ atrium-api/src/app/bsky/feed/generator.rs | 3 + atrium-api/src/app/bsky/feed/search_posts.rs | 27 ++++++++ .../src/app/bsky/feed/send_interactions.rs | 18 ++++++ atrium-api/src/app/bsky/unspecced.rs | 1 + .../unspecced/get_suggestions_skeleton.rs | 28 +++++++++ .../bsky/unspecced/search_actors_skeleton.rs | 3 + .../bsky/unspecced/search_posts_skeleton.rs | 30 +++++++++ atrium-api/src/client.rs | 62 +++++++++++++++++++ atrium-api/src/com/atproto/sync/get_record.rs | 2 +- 13 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 atrium-api/src/app/bsky/feed/send_interactions.rs create mode 100644 atrium-api/src/app/bsky/unspecced/get_suggestions_skeleton.rs diff --git a/atrium-api/src/app/bsky/actor/defs.rs b/atrium-api/src/app/bsky/actor/defs.rs index a553f374..b0437c3d 100644 --- a/atrium-api/src/app/bsky/actor/defs.rs +++ b/atrium-api/src/app/bsky/actor/defs.rs @@ -94,6 +94,8 @@ pub struct ProfileAssociated { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ProfileView { + #[serde(skip_serializing_if = "Option::is_none")] + pub associated: Option, #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -112,6 +114,8 @@ pub struct ProfileView { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ProfileViewBasic { + #[serde(skip_serializing_if = "Option::is_none")] + pub associated: Option, #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, pub did: crate::types::string::Did, diff --git a/atrium-api/src/app/bsky/embed/record.rs b/atrium-api/src/app/bsky/embed/record.rs index 54afdded..f7d5d87a 100644 --- a/atrium-api/src/app/bsky/embed/record.rs +++ b/atrium-api/src/app/bsky/embed/record.rs @@ -34,6 +34,12 @@ pub struct ViewRecord { pub indexed_at: crate::types::string::Datetime, #[serde(skip_serializing_if = "Option::is_none")] pub labels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub like_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reply_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repost_count: Option, pub uri: String, ///The record data itself. pub value: crate::records::Record, diff --git a/atrium-api/src/app/bsky/feed.rs b/atrium-api/src/app/bsky/feed.rs index 2ba361f5..826357db 100644 --- a/atrium-api/src/app/bsky/feed.rs +++ b/atrium-api/src/app/bsky/feed.rs @@ -21,6 +21,7 @@ pub mod like; pub mod post; pub mod repost; pub mod search_posts; +pub mod send_interactions; pub mod threadgate; #[derive(Debug)] pub struct Generator; diff --git a/atrium-api/src/app/bsky/feed/defs.rs b/atrium-api/src/app/bsky/feed/defs.rs index b5e8fb1e..25a4d15e 100644 --- a/atrium-api/src/app/bsky/feed/defs.rs +++ b/atrium-api/src/app/bsky/feed/defs.rs @@ -14,9 +14,20 @@ pub struct BlockedPost { pub blocked: bool, pub uri: String, } +///User clicked through to the author of the feed item +pub struct ClickthroughAuthor; +///User clicked through to the embedded content of the feed item +pub struct ClickthroughEmbed; +///User clicked through to the feed item +pub struct ClickthroughItem; +///User clicked through to the reposter of the feed item +pub struct ClickthroughReposter; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct FeedViewPost { + ///Context provided by feed generator that may be passed back alongside interactions. + #[serde(skip_serializing_if = "Option::is_none")] + pub feed_context: Option, pub post: PostView, #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option>, @@ -26,6 +37,8 @@ pub struct FeedViewPost { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct GeneratorView { + #[serde(skip_serializing_if = "Option::is_none")] + pub accepts_interactions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, pub cid: crate::types::string::Cid, @@ -53,6 +66,29 @@ pub struct GeneratorViewerState { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +pub struct Interaction { + #[serde(skip_serializing_if = "Option::is_none")] + pub event: Option, + ///Context on a feed item that was orginally supplied by the feed generator on getFeedSkeleton. + #[serde(skip_serializing_if = "Option::is_none")] + pub feed_context: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub item: Option, +} +///User liked the feed item +pub struct InteractionLike; +///User quoted the feed item +pub struct InteractionQuote; +///User replied to the feed item +pub struct InteractionReply; +///User reposted the feed item +pub struct InteractionRepost; +///Feed item was seen by user +pub struct InteractionSeen; +///User shared the feed item +pub struct InteractionShare; +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct NotFoundPost { pub not_found: bool, pub uri: String, @@ -92,9 +128,16 @@ pub struct ReplyRef { pub parent: crate::types::Union, pub root: crate::types::Union, } +///Request that less content like the given feed item be shown in the feed +pub struct RequestLess; +///Request that more content like the given feed item be shown in the feed +pub struct RequestMore; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct SkeletonFeedPost { + ///Context that will be passed through to client and may be passed to feed generator back alongside interactions. + #[serde(skip_serializing_if = "Option::is_none")] + pub feed_context: Option, pub post: String, #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option>, diff --git a/atrium-api/src/app/bsky/feed/generator.rs b/atrium-api/src/app/bsky/feed/generator.rs index 264fa4e6..d6c6b137 100644 --- a/atrium-api/src/app/bsky/feed/generator.rs +++ b/atrium-api/src/app/bsky/feed/generator.rs @@ -3,6 +3,9 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Record { + ///Declaration that a feed accepts feedback interactions from a client through app.bsky.feed.sendInteractions + #[serde(skip_serializing_if = "Option::is_none")] + pub accepts_interactions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, pub created_at: crate::types::string::Datetime, diff --git a/atrium-api/src/app/bsky/feed/search_posts.rs b/atrium-api/src/app/bsky/feed/search_posts.rs index bdc8b689..035db0d8 100644 --- a/atrium-api/src/app/bsky/feed/search_posts.rs +++ b/atrium-api/src/app/bsky/feed/search_posts.rs @@ -3,13 +3,40 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { + ///Filter to posts by the given account. Handles are resolved to DID before query-time. + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option, ///Optional pagination mechanism; may not necessarily allow scrolling through entire result set. #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, + ///Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization. + #[serde(skip_serializing_if = "Option::is_none")] + pub domain: Option, + ///Filter to posts in the given language. Expected to be based on post language field, though server may override language detection. + #[serde(skip_serializing_if = "Option::is_none")] + pub lang: Option, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option>, + ///Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions. + #[serde(skip_serializing_if = "Option::is_none")] + pub mentions: Option, ///Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. pub q: String, + ///Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD). + #[serde(skip_serializing_if = "Option::is_none")] + pub since: Option, + ///Specifies the ranking order of results. + #[serde(skip_serializing_if = "Option::is_none")] + pub sort: Option, + ///Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching. + #[serde(skip_serializing_if = "Option::is_none")] + pub tag: Option>, + ///Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD). + #[serde(skip_serializing_if = "Option::is_none")] + pub until: Option, + ///Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/app/bsky/feed/send_interactions.rs b/atrium-api/src/app/bsky/feed/send_interactions.rs new file mode 100644 index 00000000..79d575de --- /dev/null +++ b/atrium-api/src/app/bsky/feed/send_interactions.rs @@ -0,0 +1,18 @@ +// This file is generated by atrium-codegen. DO NOT EDIT. +//!Definitions for the `app.bsky.feed.sendInteractions` namespace. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Input { + pub interactions: Vec, +} +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Output {} +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(tag = "error", content = "message")] +pub enum Error {} +impl std::fmt::Display for Error { + fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result { + Ok(()) + } +} diff --git a/atrium-api/src/app/bsky/unspecced.rs b/atrium-api/src/app/bsky/unspecced.rs index 9c8d0529..1835c2a0 100644 --- a/atrium-api/src/app/bsky/unspecced.rs +++ b/atrium-api/src/app/bsky/unspecced.rs @@ -2,6 +2,7 @@ //!Definitions for the `app.bsky.unspecced` namespace. pub mod defs; pub mod get_popular_feed_generators; +pub mod get_suggestions_skeleton; pub mod get_tagged_suggestions; pub mod search_actors_skeleton; pub mod search_posts_skeleton; diff --git a/atrium-api/src/app/bsky/unspecced/get_suggestions_skeleton.rs b/atrium-api/src/app/bsky/unspecced/get_suggestions_skeleton.rs new file mode 100644 index 00000000..0cc0af0b --- /dev/null +++ b/atrium-api/src/app/bsky/unspecced/get_suggestions_skeleton.rs @@ -0,0 +1,28 @@ +// This file is generated by atrium-codegen. DO NOT EDIT. +//!Definitions for the `app.bsky.unspecced.getSuggestionsSkeleton` namespace. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Parameters { + #[serde(skip_serializing_if = "Option::is_none")] + pub cursor: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option>, + ///DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. + #[serde(skip_serializing_if = "Option::is_none")] + pub viewer: Option, +} +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Output { + pub actors: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub cursor: Option, +} +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(tag = "error", content = "message")] +pub enum Error {} +impl std::fmt::Display for Error { + fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result { + Ok(()) + } +} diff --git a/atrium-api/src/app/bsky/unspecced/search_actors_skeleton.rs b/atrium-api/src/app/bsky/unspecced/search_actors_skeleton.rs index c3fe2ac7..7b8d2ff9 100644 --- a/atrium-api/src/app/bsky/unspecced/search_actors_skeleton.rs +++ b/atrium-api/src/app/bsky/unspecced/search_actors_skeleton.rs @@ -13,6 +13,9 @@ pub struct Parameters { ///If true, acts as fast/simple 'typeahead' query. #[serde(skip_serializing_if = "Option::is_none")] pub typeahead: Option, + ///DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. + #[serde(skip_serializing_if = "Option::is_none")] + pub viewer: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/app/bsky/unspecced/search_posts_skeleton.rs b/atrium-api/src/app/bsky/unspecced/search_posts_skeleton.rs index 7523af9c..ae96e9d0 100644 --- a/atrium-api/src/app/bsky/unspecced/search_posts_skeleton.rs +++ b/atrium-api/src/app/bsky/unspecced/search_posts_skeleton.rs @@ -3,13 +3,43 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { + ///Filter to posts by the given account. Handles are resolved to DID before query-time. + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option, ///Optional pagination mechanism; may not necessarily allow scrolling through entire result set. #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, + ///Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization. + #[serde(skip_serializing_if = "Option::is_none")] + pub domain: Option, + ///Filter to posts in the given language. Expected to be based on post language field, though server may override language detection. + #[serde(skip_serializing_if = "Option::is_none")] + pub lang: Option, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option>, + ///Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions. + #[serde(skip_serializing_if = "Option::is_none")] + pub mentions: Option, ///Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. pub q: String, + ///Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD). + #[serde(skip_serializing_if = "Option::is_none")] + pub since: Option, + ///Specifies the ranking order of results. + #[serde(skip_serializing_if = "Option::is_none")] + pub sort: Option, + ///Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching. + #[serde(skip_serializing_if = "Option::is_none")] + pub tag: Option>, + ///Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD). + #[serde(skip_serializing_if = "Option::is_none")] + pub until: Option, + ///Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + ///DID of the account making the request (not included for public/unauthenticated queries). Used for 'from:me' queries. + #[serde(skip_serializing_if = "Option::is_none")] + pub viewer: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/client.rs b/atrium-api/src/client.rs index 9f5b0811..8b7c2883 100644 --- a/atrium-api/src/client.rs +++ b/atrium-api/src/client.rs @@ -959,6 +959,36 @@ where _ => Err(atrium_xrpc::error::Error::UnexpectedResponseType), } } + ///Send information about interactions with feed items back to the feed generator that served them. + pub async fn send_interactions( + &self, + input: crate::app::bsky::feed::send_interactions::Input, + ) -> Result< + crate::app::bsky::feed::send_interactions::Output, + atrium_xrpc::error::Error, + > { + let response = self + .xrpc + .send_xrpc::< + (), + _, + _, + _, + >( + &atrium_xrpc::XrpcRequest { + method: http::Method::POST, + path: "app.bsky.feed.sendInteractions".into(), + parameters: None, + input: Some(atrium_xrpc::InputDataOrBytes::Data(input)), + encoding: Some(String::from("application/json")), + }, + ) + .await?; + match response { + atrium_xrpc::OutputDataOrBytes::Data(data) => Ok(data), + _ => Err(atrium_xrpc::error::Error::UnexpectedResponseType), + } + } } impl app::bsky::graph::Service where @@ -1599,6 +1629,38 @@ where _ => Err(atrium_xrpc::error::Error::UnexpectedResponseType), } } + ///Get a skeleton of suggested actors. Intended to be called and then hydrated through app.bsky.actor.getSuggestions + pub async fn get_suggestions_skeleton( + &self, + params: crate::app::bsky::unspecced::get_suggestions_skeleton::Parameters, + ) -> Result< + crate::app::bsky::unspecced::get_suggestions_skeleton::Output, + atrium_xrpc::error::Error< + crate::app::bsky::unspecced::get_suggestions_skeleton::Error, + >, + > { + let response = self + .xrpc + .send_xrpc::< + _, + (), + _, + _, + >( + &atrium_xrpc::XrpcRequest { + method: http::Method::GET, + path: "app.bsky.unspecced.getSuggestionsSkeleton".into(), + parameters: Some(params), + input: None, + encoding: None, + }, + ) + .await?; + match response { + atrium_xrpc::OutputDataOrBytes::Data(data) => Ok(data), + _ => Err(atrium_xrpc::error::Error::UnexpectedResponseType), + } + } ///Get a list of suggestions (feeds and users) tagged with categories pub async fn get_tagged_suggestions( &self, diff --git a/atrium-api/src/com/atproto/sync/get_record.rs b/atrium-api/src/com/atproto/sync/get_record.rs index 5142e745..c8b2f03b 100644 --- a/atrium-api/src/com/atproto/sync/get_record.rs +++ b/atrium-api/src/com/atproto/sync/get_record.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { pub collection: crate::types::string::Nsid, - ///An optional past commit CID. + ///DEPRECATED: referenced a repo commit by CID, and retrieved record as of that commit #[serde(skip_serializing_if = "Option::is_none")] pub commit: Option, ///The DID of the repo.