From 36b9580030998958d172aef202c95e6776652699 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 18:48:25 +0000 Subject: [PATCH 01/27] feat: implement StepSearchActors --- docs/guide/features.md | 2 +- lib/core/StepSearchActors.ts | 110 +++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 11 ++++ lib/trotsky.ts | 1 + 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepSearchActors.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 12ab531..768a1cb 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -37,6 +37,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepStarterPacks** | :white_check_mark: | Get a list of starter packs by their URIs. | ```Trotsky.init(agent).starterPacks([uri1, uri2]).each()``` **StepStreamPosts** | :test_tube: | Stream posts from the firehose. | ```Trotsky.init(agent).streamPosts().each()``` **StepTimeline** | :white_check_mark: | Get the authenticated user's timeline. | ```Trotsky.init(agent).timeline().take(20).each()``` + **StepSearchActors** | :white_check_mark: | Search for actors by name/handle. | ```Trotsky.init(agent).searchActors({ q: "typescript" }).each()``` ## Planned Features @@ -65,7 +66,6 @@ The following features are planned for future implementation: **StepPostThread** | :construction: | Get a full post thread with replies. | `app.bsky.feed.getPostThread` **StepPostUnlike** | :construction: | Unlike a post. | Delete like record **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record - **StepSearchActors** | :construction: | Search for actors by name/handle. | `app.bsky.actor.searchActors` **StepSuggestedFeeds** | :construction: | Get suggested custom feeds. | `app.bsky.feed.getSuggestedFeeds` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` **StepThreadUnmute** | :construction: | Unmute a thread. | `app.bsky.graph.unmuteThread` diff --git a/lib/core/StepSearchActors.ts b/lib/core/StepSearchActors.ts new file mode 100644 index 0000000..524bbd2 --- /dev/null +++ b/lib/core/StepSearchActors.ts @@ -0,0 +1,110 @@ +import type { AppBskyActorSearchActors, AtpAgent } from "@atproto/api" + +import { StepActors } from "../trotsky" + +/** + * Represents the output of a search actors step, consisting of an array of actor profiles. + * @public + */ +export type StepSearchActorsOutput = AppBskyActorSearchActors.OutputSchema["actors"] + +/** + * Represents the query parameters for searching actors. + * @public + */ +export type StepSearchActorsQueryParams = AppBskyActorSearchActors.QueryParams + +/** + * Represents the cursor for paginating through search actor results. + * @public + */ +export type StepSearchActorsQueryParamsCursor = StepSearchActorsQueryParams["cursor"] | undefined + +/** + * Represents a step for searching actors on Bluesky, with support for pagination. + * + * @typeParam P - The parent type of this step. + * @typeParam C - The child context type, defaulting to `null`. + * @typeParam O - The output type, defaulting to {@link StepSearchActorsOutput}. + * + * @example + * Search for actors and display them: + * ```ts + * await Trotsky.init(agent) + * .searchActors({ q: "typescript" }) + * .take(10) + * .each() + * .tap((step) => { + * console.log(step.context.handle) + * console.log(`Followers: ${step.context.followersCount}`) + * }) + * .run() + * ``` + * + * @example + * Follow actors matching specific criteria: + * ```ts + * await Trotsky.init(agent) + * .searchActors({ q: "bluesky" }) + * .take(20) + * .each() + * .when((step) => step?.context?.followersCount > 100) + * .follow() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepSearchActors extends StepActors { + + /** + * The initial query parameters for the search. + */ + _queryParams: StepSearchActorsQueryParams + + /** + * Initializes the StepSearchActors instance with the given agent, parent, and query parameters. + * + * @param agent - The AT protocol agent used for API calls. + * @param parent - The parent step in the chain. + * @param queryParams - The initial query parameters for the search. + */ + constructor (agent: AtpAgent, parent: P, queryParams: StepSearchActorsQueryParams) { + super(agent, parent, []) + this._queryParams = queryParams + } + + /** + * Clones the current step and returns a new instance with the same parameters. + * @returns A new {@link StepSearchActors} instance. + */ + override clone () { + return super.clone(this._queryParams) + } + + /** + * Applies the pagination logic to retrieve actors based on the search parameters. + * Results are stored in the `output` property. + * + * @override + */ + async applyPagination () { + this.output = await this.paginate("actors", (cursor) => { + return this + .agent + .app.bsky.actor + .searchActors(this.queryParams(cursor)) + }) + } + + /** + * Constructs the query parameters for the current pagination step. + * + * @param cursor - The cursor indicating the current position in the paginated results. + * @returns The query parameters for the search. + */ + queryParams (cursor: StepSearchActorsQueryParamsCursor): StepSearchActorsQueryParams { + return { ...this._queryParams, cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 74c99fa..9884e14 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -12,6 +12,7 @@ import type { Resolvable } from "./utils/resolvable" import type { StepStarterPackUri } from "./StepStarterPack" import type { StepStarterPacksUris } from "./StepStarterPacks" import type { StepSearchStarterPacksQueryParams } from "./StepSearchStarterPacks" +import type { StepSearchActorsQueryParams } from "./StepSearchActors" import { StepActor, @@ -23,6 +24,7 @@ import { StepListUri, StepStarterPack, StepStarterPacks, + StepSearchActors, StepSearchPosts, StepSearchStarterPacks, StepStreamPosts, @@ -126,6 +128,15 @@ export class Trotsky extends StepBuilder { return this.append(StepSearchPosts, queryParams) } + /** + * Adds a {@link StepSearchActors} step. + * @param queryParams - Search parameters. + * @returns The new {@link StepSearchActors} instance. + */ + searchActors (queryParams: StepSearchActorsQueryParams): StepSearchActors { + return this.append(StepSearchActors, queryParams) + } + /** * Adds a {@link StepSearchStarterPacks} step. * @param queryParams - Search parameters. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index f25fbb4..35eb123 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -60,6 +60,7 @@ export * from "./core/StepStreamPosts" // List of actors export * from "./core/StepActors" export * from "./core/StepActorsEntry" +export * from "./core/StepSearchActors" // List of posts export * from "./core/StepPosts" From eb4f3dde9fb455cdadaff8b1092acbc5b07bc2aa Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 18:51:32 +0000 Subject: [PATCH 02/27] feat: implement StepPostLikers --- docs/guide/features.md | 2 +- lib/core/StepPostLikers.ts | 92 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 13 ++++- lib/trotsky.ts | 1 + 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 lib/core/StepPostLikers.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 768a1cb..ea471fd 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -38,6 +38,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepStreamPosts** | :test_tube: | Stream posts from the firehose. | ```Trotsky.init(agent).streamPosts().each()``` **StepTimeline** | :white_check_mark: | Get the authenticated user's timeline. | ```Trotsky.init(agent).timeline().take(20).each()``` **StepSearchActors** | :white_check_mark: | Search for actors by name/handle. | ```Trotsky.init(agent).searchActors({ q: "typescript" }).each()``` + **StepPostLikers** | :white_check_mark: | Get actors who liked a post. | ```Trotsky.init(agent).post("at://...").likers().each()``` ## Planned Features @@ -60,7 +61,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepPostLikers** | :construction: | Get actors who liked a post. | `app.bsky.feed.getLikes` **StepPostQuotes** | :construction: | Get quote posts of a post. | `app.bsky.feed.getQuotes` **StepPostReposters** | :construction: | Get actors who reposted a post. | `app.bsky.feed.getRepostedBy` **StepPostThread** | :construction: | Get a full post thread with replies. | `app.bsky.feed.getPostThread` diff --git a/lib/core/StepPostLikers.ts b/lib/core/StepPostLikers.ts new file mode 100644 index 0000000..fdd26b3 --- /dev/null +++ b/lib/core/StepPostLikers.ts @@ -0,0 +1,92 @@ +import type { AppBskyFeedGetLikes } from "@atproto/api" + +import { + StepActors, + type StepActorsOutput, + type StepPost, + type StepPostOutput +} from "../trotsky" + +/** + * Type representing the output of the likers retrieved by {@link StepPostLikers}. + * Each entry contains the actor who liked the post. + * @public + */ +export type StepPostLikersOutput = StepActorsOutput + +/** + * Type representing the query parameters for retrieving post likers. + * @public + */ +export type StepPostLikersQueryParams = AppBskyFeedGetLikes.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepPostLikersQueryParamsCursor = StepPostLikersQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving actors who liked a post using the Bluesky API. + * Supports paginated retrieval of likers. + * + * @typeParam P - Type of the parent step, extending {@link StepPost}. + * @typeParam C - Type of the context object, extending {@link StepPostOutput}. + * @typeParam O - Type of the output object, extending {@link StepPostLikersOutput}. + * + * @example + * Get and iterate through users who liked a post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .likers() + * .each() + * .tap((step) => { + * console.log(`Liked by: ${step.context.actor.handle}`) + * }) + * .run() + * ``` + * + * @example + * Follow users who liked a specific post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .likers() + * .take(10) + * .each() + * .follow() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepPostLikers

extends StepActors { + + /** + * Applies pagination to retrieve likers and sets the output. + * Fetches paginated results using the agent and extracts actor profiles. + */ + async applyPagination () { + const likes = await this.paginate("likes", (cursor) => { + return this.agent.app.bsky.feed.getLikes(this.queryParams(cursor)) + }) + // Extract actor profiles from like objects + this.output = likes.map((like: AppBskyFeedGetLikes.Like) => like.actor) as O + } + + /** + * Generates query parameters for retrieving likers, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving likers. + * @throws Error if no context is found. + */ + private queryParams (cursor: StepPostLikersQueryParamsCursor): StepPostLikersQueryParams { + if (!this.context) { + throw new Error("No context found for StepPostLikers") + } + + return { "uri": this.context.uri, cursor } + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index afa78d1..bf8f820 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostReply, StepPostRepost } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostReply, StepPostRepost } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -56,10 +56,19 @@ export abstract class PostMixins extends Step { /** * Appends a step to retrieve the author of the current post. - * + * * @returns The appended {@link StepPostAuthor} instance. */ author (): StepPostAuthor { return this.append(StepPostAuthor) } + + /** + * Appends a step to retrieve the actors who liked the current post. + * + * @returns The appended {@link StepPostLikers} instance. + */ + likers (): StepPostLikers { + return this.append(StepPostLikers) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 35eb123..63c6c4b 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -93,6 +93,7 @@ export * from "./core/StepCreatePost" export * from "./core/StepPost" export * from "./core/StepPostAuthor" export * from "./core/StepPostLike" +export * from "./core/StepPostLikers" export * from "./core/StepPostReply" export * from "./core/StepPostRepost" From dd9c6d8915f913749593be027243754a849a96ea Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 18:53:15 +0000 Subject: [PATCH 03/27] feat: implement StepPostReposters --- docs/guide/features.md | 2 +- lib/core/StepPostReposters.ts | 89 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 11 ++++- lib/trotsky.ts | 1 + 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepPostReposters.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index ea471fd..9d97c28 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -39,6 +39,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepTimeline** | :white_check_mark: | Get the authenticated user's timeline. | ```Trotsky.init(agent).timeline().take(20).each()``` **StepSearchActors** | :white_check_mark: | Search for actors by name/handle. | ```Trotsky.init(agent).searchActors({ q: "typescript" }).each()``` **StepPostLikers** | :white_check_mark: | Get actors who liked a post. | ```Trotsky.init(agent).post("at://...").likers().each()``` + **StepPostReposters** | :white_check_mark: | Get actors who reposted a post. | ```Trotsky.init(agent).post("at://...").reposters().each()``` ## Planned Features @@ -62,7 +63,6 @@ The following features are planned for future implementation: **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` **StepPostQuotes** | :construction: | Get quote posts of a post. | `app.bsky.feed.getQuotes` - **StepPostReposters** | :construction: | Get actors who reposted a post. | `app.bsky.feed.getRepostedBy` **StepPostThread** | :construction: | Get a full post thread with replies. | `app.bsky.feed.getPostThread` **StepPostUnlike** | :construction: | Unlike a post. | Delete like record **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record diff --git a/lib/core/StepPostReposters.ts b/lib/core/StepPostReposters.ts new file mode 100644 index 0000000..908d703 --- /dev/null +++ b/lib/core/StepPostReposters.ts @@ -0,0 +1,89 @@ +import type { AppBskyFeedGetRepostedBy } from "@atproto/api" + +import { + StepActors, + type StepActorsOutput, + type StepPost, + type StepPostOutput +} from "../trotsky" + +/** + * Type representing the output of the reposters retrieved by {@link StepPostReposters}. + * @public + */ +export type StepPostRepostersOutput = StepActorsOutput + +/** + * Type representing the query parameters for retrieving post reposters. + * @public + */ +export type StepPostRepostersQueryParams = AppBskyFeedGetRepostedBy.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepPostRepostersQueryParamsCursor = StepPostRepostersQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving actors who reposted a post using the Bluesky API. + * Supports paginated retrieval of reposters. + * + * @typeParam P - Type of the parent step, extending {@link StepPost}. + * @typeParam C - Type of the context object, extending {@link StepPostOutput}. + * @typeParam O - Type of the output object, extending {@link StepPostRepostersOutput}. + * + * @example + * Get and iterate through users who reposted a post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .reposters() + * .each() + * .tap((step) => { + * console.log(`Reposted by: ${step.context.handle}`) + * }) + * .run() + * ``` + * + * @example + * Follow users who reposted a specific post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .reposters() + * .take(10) + * .each() + * .follow() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepPostReposters

extends StepActors { + + /** + * Applies pagination to retrieve reposters and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("repostedBy", (cursor) => { + return this.agent.app.bsky.feed.getRepostedBy(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving reposters, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving reposters. + * @throws Error if no context is found. + */ + private queryParams (cursor: StepPostRepostersQueryParamsCursor): StepPostRepostersQueryParams { + if (!this.context) { + throw new Error("No context found for StepPostReposters") + } + + return { "uri": this.context.uri, cursor } + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index bf8f820..b0c57d7 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostReply, StepPostRepost } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostReply, StepPostRepost, StepPostReposters } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -71,4 +71,13 @@ export abstract class PostMixins extends Step { likers (): StepPostLikers { return this.append(StepPostLikers) } + + /** + * Appends a step to retrieve the actors who reposted the current post. + * + * @returns The appended {@link StepPostReposters} instance. + */ + reposters (): StepPostReposters { + return this.append(StepPostReposters) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 63c6c4b..b801d1c 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -96,6 +96,7 @@ export * from "./core/StepPostLike" export * from "./core/StepPostLikers" export * from "./core/StepPostReply" export * from "./core/StepPostRepost" +export * from "./core/StepPostReposters" // Single lists export * from "./core/StepList" From c3116e64c0cf681bc7dad6ee952240dd16931301 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 18:55:01 +0000 Subject: [PATCH 04/27] feat: implement StepPostQuotes --- docs/guide/features.md | 2 +- lib/core/StepPostQuotes.ts | 90 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 11 ++++- lib/trotsky.ts | 1 + 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepPostQuotes.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 9d97c28..f492d2d 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -40,6 +40,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepSearchActors** | :white_check_mark: | Search for actors by name/handle. | ```Trotsky.init(agent).searchActors({ q: "typescript" }).each()``` **StepPostLikers** | :white_check_mark: | Get actors who liked a post. | ```Trotsky.init(agent).post("at://...").likers().each()``` **StepPostReposters** | :white_check_mark: | Get actors who reposted a post. | ```Trotsky.init(agent).post("at://...").reposters().each()``` + **StepPostQuotes** | :white_check_mark: | Get quote posts of a post. | ```Trotsky.init(agent).post("at://...").quotes().each()``` ## Planned Features @@ -62,7 +63,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepPostQuotes** | :construction: | Get quote posts of a post. | `app.bsky.feed.getQuotes` **StepPostThread** | :construction: | Get a full post thread with replies. | `app.bsky.feed.getPostThread` **StepPostUnlike** | :construction: | Unlike a post. | Delete like record **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record diff --git a/lib/core/StepPostQuotes.ts b/lib/core/StepPostQuotes.ts new file mode 100644 index 0000000..b140d34 --- /dev/null +++ b/lib/core/StepPostQuotes.ts @@ -0,0 +1,90 @@ +import type { AppBskyFeedGetQuotes } from "@atproto/api" + +import { + StepPosts, + type StepPostsOutput, + type StepPost, + type StepPostOutput +} from "../trotsky" + +/** + * Type representing the output of the quotes retrieved by {@link StepPostQuotes}. + * @public + */ +export type StepPostQuotesOutput = StepPostsOutput + +/** + * Type representing the query parameters for retrieving post quotes. + * @public + */ +export type StepPostQuotesQueryParams = AppBskyFeedGetQuotes.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepPostQuotesQueryParamsCursor = StepPostQuotesQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving quote posts of a post using the Bluesky API. + * Supports paginated retrieval of quote posts. + * + * @typeParam P - Type of the parent step, extending {@link StepPost}. + * @typeParam C - Type of the context object, extending {@link StepPostOutput}. + * @typeParam O - Type of the output object, extending {@link StepPostQuotesOutput}. + * + * @example + * Get and iterate through quote posts: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .quotes() + * .each() + * .tap((step) => { + * console.log(`Quote by: ${step.context.author.handle}`) + * console.log(`Text: ${step.context.record.text}`) + * }) + * .run() + * ``` + * + * @example + * Like all quote posts: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .quotes() + * .take(10) + * .each() + * .like() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepPostQuotes

extends StepPosts { + + /** + * Applies pagination to retrieve quote posts and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("posts", (cursor) => { + return this.agent.app.bsky.feed.getQuotes(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving quotes, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving quotes. + * @throws Error if no context is found. + */ + private queryParams (cursor: StepPostQuotesQueryParamsCursor): StepPostQuotesQueryParams { + if (!this.context) { + throw new Error("No context found for StepPostQuotes") + } + + return { "uri": this.context.uri, cursor } + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index b0c57d7..a8e53b2 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostReply, StepPostRepost, StepPostReposters } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -80,4 +80,13 @@ export abstract class PostMixins extends Step { reposters (): StepPostReposters { return this.append(StepPostReposters) } + + /** + * Appends a step to retrieve the quote posts of the current post. + * + * @returns The appended {@link StepPostQuotes} instance. + */ + quotes (): StepPostQuotes { + return this.append(StepPostQuotes) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index b801d1c..64263cf 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -94,6 +94,7 @@ export * from "./core/StepPost" export * from "./core/StepPostAuthor" export * from "./core/StepPostLike" export * from "./core/StepPostLikers" +export * from "./core/StepPostQuotes" export * from "./core/StepPostReply" export * from "./core/StepPostRepost" export * from "./core/StepPostReposters" From 1bdc4e4d14958002d202f664e40b29810398630f Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 18:57:18 +0000 Subject: [PATCH 05/27] feat: implement StepPostThread --- docs/guide/features.md | 2 +- lib/core/StepPostThread.ts | 99 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 12 ++++- lib/trotsky.ts | 1 + 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepPostThread.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index f492d2d..6aa48b7 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -41,6 +41,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostLikers** | :white_check_mark: | Get actors who liked a post. | ```Trotsky.init(agent).post("at://...").likers().each()``` **StepPostReposters** | :white_check_mark: | Get actors who reposted a post. | ```Trotsky.init(agent).post("at://...").reposters().each()``` **StepPostQuotes** | :white_check_mark: | Get quote posts of a post. | ```Trotsky.init(agent).post("at://...").quotes().each()``` + **StepPostThread** | :white_check_mark: | Get a full post thread with replies. | ```Trotsky.init(agent).post("at://...").thread()``` ## Planned Features @@ -63,7 +64,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepPostThread** | :construction: | Get a full post thread with replies. | `app.bsky.feed.getPostThread` **StepPostUnlike** | :construction: | Unlike a post. | Delete like record **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record **StepSuggestedFeeds** | :construction: | Get suggested custom feeds. | `app.bsky.feed.getSuggestedFeeds` diff --git a/lib/core/StepPostThread.ts b/lib/core/StepPostThread.ts new file mode 100644 index 0000000..4b8371b --- /dev/null +++ b/lib/core/StepPostThread.ts @@ -0,0 +1,99 @@ +import type { AppBskyFeedGetPostThread } from "@atproto/api" + +import { + Step, + type StepPost, + type StepPostOutput +} from "../trotsky" + +/** + * Type representing the output of the thread retrieved by {@link StepPostThread}. + * @public + */ +export type StepPostThreadOutput = AppBskyFeedGetPostThread.OutputSchema + +/** + * Type representing the query parameters for retrieving a post thread. + * @public + */ +export type StepPostThreadQueryParams = AppBskyFeedGetPostThread.QueryParams + +/** + * Represents a step for retrieving a full post thread with replies using the Bluesky API. + * + * @typeParam P - Type of the parent step, extending {@link StepPost}. + * @typeParam C - Type of the context object, extending {@link StepPostOutput}. + * @typeParam O - Type of the output object, extending {@link StepPostThreadOutput}. + * + * @example + * Get a post thread with replies: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .thread() + * .tap((step) => { + * const thread = step.output.thread as AppBskyFeedDefs.ThreadViewPost + * console.log(`Thread by: ${thread.post.author.handle}`) + * console.log(`Replies: ${thread.replies?.length || 0}`) + * }) + * .run() + * ``` + * + * @example + * Get a thread with custom depth: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .thread({ depth: 10, parentHeight: 5 }) + * .tap((step) => { + * console.log(step.output) + * }) + * .run() + * ``` + * + * @public + */ +export class StepPostThread

extends Step { + + /** + * Additional query parameters for the thread request (depth, parentHeight). + */ + _options: Partial + + /** + * Initializes the StepPostThread instance. + * + * @param options - Optional parameters for thread retrieval (depth, parentHeight). + */ + constructor (...args: unknown[]) { + const [agent, parent, options = {}] = args as [ConstructorParameters[0], P, Partial?] + super(agent, parent) + this._options = options + } + + /** + * Clones the current step and returns a new instance with the same parameters. + * @returns A new {@link StepPostThread} instance. + */ + override clone () { + return super.clone(this._options) + } + + /** + * Applies the step logic to retrieve the thread. + * + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepPostThread") + } + + const { data } = await this.agent.app.bsky.feed.getPostThread({ + "uri": this.context.uri, + ...this._options + }) + + this.output = data as O + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index a8e53b2..21b996e 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, type StepPostThreadQueryParams } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -89,4 +89,14 @@ export abstract class PostMixins extends Step { quotes (): StepPostQuotes { return this.append(StepPostQuotes) } + + /** + * Appends a step to retrieve the full thread of the current post. + * + * @param options - Optional parameters for thread depth and parent height. + * @returns The appended {@link StepPostThread} instance. + */ + thread (options?: Partial): StepPostThread { + return this.append(StepPostThread, options) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 64263cf..f010df1 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -98,6 +98,7 @@ export * from "./core/StepPostQuotes" export * from "./core/StepPostReply" export * from "./core/StepPostRepost" export * from "./core/StepPostReposters" +export * from "./core/StepPostThread" // Single lists export * from "./core/StepList" From e340b18a1f92f5eb9e2030c1a089e6c2bfdee6ce Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:00:30 +0000 Subject: [PATCH 06/27] feat: implement StepPostUnlike --- docs/guide/features.md | 2 +- lib/core/StepPostUnlike.ts | 57 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 11 ++++++- lib/trotsky.ts | 1 + 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepPostUnlike.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 6aa48b7..af5d389 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -42,6 +42,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostReposters** | :white_check_mark: | Get actors who reposted a post. | ```Trotsky.init(agent).post("at://...").reposters().each()``` **StepPostQuotes** | :white_check_mark: | Get quote posts of a post. | ```Trotsky.init(agent).post("at://...").quotes().each()``` **StepPostThread** | :white_check_mark: | Get a full post thread with replies. | ```Trotsky.init(agent).post("at://...").thread()``` + **StepPostUnlike** | :white_check_mark: | Unlike a post. | ```Trotsky.init(agent).post("at://...").unlike()``` ## Planned Features @@ -64,7 +65,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepPostUnlike** | :construction: | Unlike a post. | Delete like record **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record **StepSuggestedFeeds** | :construction: | Get suggested custom feeds. | `app.bsky.feed.getSuggestedFeeds` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` diff --git a/lib/core/StepPostUnlike.ts b/lib/core/StepPostUnlike.ts new file mode 100644 index 0000000..4888502 --- /dev/null +++ b/lib/core/StepPostUnlike.ts @@ -0,0 +1,57 @@ +import type { AppBskyFeedDefs } from "@atproto/api" + +import { Step, type StepPost, type StepPostOutput } from "../trotsky" + +/** + * Represents a step for unliking a specific post. + * + * @typeParam P - The parent type of this step, defaulting to {@link StepPost}. + * @typeParam C - The context type, defaulting to {@link StepPostOutput}. + * @typeParam O - The output type, defaulting to `null`. + * + * @example + * Unlike a specific post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l") + * .unlike() + * .run() + * ``` + * + * @example + * Unlike posts from search results: + * ```ts + * await Trotsky.init(agent) + * .searchPosts({ q: "typescript" }) + * .take(10) + * .each() + * .when((step) => step?.context?.viewer?.like) + * .unlike() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepPostUnlike

extends Step { + + /** + * Applies the step logic to unlike the post provided in the context. + * Only unlikes if the user has previously liked the post. + * + * @override + * @throws Error if the context does not provide the required post data. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepPostUnlike") + } + + // PostView from the API includes viewer state + const context = this.context as unknown as AppBskyFeedDefs.PostView + const likeUri = context.viewer?.like + if (likeUri) { + await this.agent.deleteLike(likeUri) + } + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index 21b996e..fe8d64d 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, type StepPostThreadQueryParams } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, StepPostUnlike, type StepPostThreadQueryParams } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -45,6 +45,15 @@ export abstract class PostMixins extends Step { return this.append(StepPostLike) } + /** + * Appends a step to unlike the current post. + * + * @returns The appended {@link StepPostUnlike} instance. + */ + unlike (): StepPostUnlike { + return this.append(StepPostUnlike) + } + /** * Appends a step to repost the current post. * diff --git a/lib/trotsky.ts b/lib/trotsky.ts index f010df1..3872c4e 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -99,6 +99,7 @@ export * from "./core/StepPostReply" export * from "./core/StepPostRepost" export * from "./core/StepPostReposters" export * from "./core/StepPostThread" +export * from "./core/StepPostUnlike" // Single lists export * from "./core/StepList" From 2137269fbd8020431bf7b25d239816090eeb4daa Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:02:11 +0000 Subject: [PATCH 07/27] feat: implement StepPostUnrepost --- docs/guide/features.md | 2 +- lib/core/StepPostUnrepost.ts | 57 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 11 ++++++- lib/trotsky.ts | 1 + 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepPostUnrepost.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index af5d389..3001c87 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -43,6 +43,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostQuotes** | :white_check_mark: | Get quote posts of a post. | ```Trotsky.init(agent).post("at://...").quotes().each()``` **StepPostThread** | :white_check_mark: | Get a full post thread with replies. | ```Trotsky.init(agent).post("at://...").thread()``` **StepPostUnlike** | :white_check_mark: | Unlike a post. | ```Trotsky.init(agent).post("at://...").unlike()``` + **StepPostUnrepost** | :white_check_mark: | Unrepost a post. | ```Trotsky.init(agent).post("at://...").unrepost()``` ## Planned Features @@ -65,7 +66,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepPostUnrepost** | :construction: | Unrepost a post. | Delete repost record **StepSuggestedFeeds** | :construction: | Get suggested custom feeds. | `app.bsky.feed.getSuggestedFeeds` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` **StepThreadUnmute** | :construction: | Unmute a thread. | `app.bsky.graph.unmuteThread` diff --git a/lib/core/StepPostUnrepost.ts b/lib/core/StepPostUnrepost.ts new file mode 100644 index 0000000..a638c0a --- /dev/null +++ b/lib/core/StepPostUnrepost.ts @@ -0,0 +1,57 @@ +import type { AppBskyFeedDefs } from "@atproto/api" + +import { Step, type StepPost, type StepPostOutput } from "../trotsky" + +/** + * Represents a step for unreposting (removing repost) a specific post. + * + * @typeParam P - The parent type of this step, defaulting to {@link StepPost}. + * @typeParam C - The context type, defaulting to {@link StepPostOutput}. + * @typeParam O - The output type, defaulting to `null`. + * + * @example + * Unrepost a specific post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l") + * .unrepost() + * .run() + * ``` + * + * @example + * Unrepost posts from search results: + * ```ts + * await Trotsky.init(agent) + * .searchPosts({ q: "typescript" }) + * .take(10) + * .each() + * .when((step) => step?.context?.viewer?.repost) + * .unrepost() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepPostUnrepost

extends Step { + + /** + * Applies the step logic to unrepost the post provided in the context. + * Only unreposts if the user has previously reposted the post. + * + * @override + * @throws Error if the context does not provide the required post data. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepPostUnrepost") + } + + // PostView from the API includes viewer state + const context = this.context as unknown as AppBskyFeedDefs.PostView + const repostUri = context.viewer?.repost + if (repostUri) { + await this.agent.deleteRepost(repostUri) + } + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index fe8d64d..fbca404 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, StepPostUnlike, type StepPostThreadQueryParams } from "../../trotsky" +import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, StepPostUnlike, StepPostUnrepost, type StepPostThreadQueryParams } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -63,6 +63,15 @@ export abstract class PostMixins extends Step { return this.append(StepPostRepost) } + /** + * Appends a step to unrepost the current post. + * + * @returns The appended {@link StepPostUnrepost} instance. + */ + unrepost (): StepPostUnrepost { + return this.append(StepPostUnrepost) + } + /** * Appends a step to retrieve the author of the current post. * diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 3872c4e..53fba6d 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -100,6 +100,7 @@ export * from "./core/StepPostRepost" export * from "./core/StepPostReposters" export * from "./core/StepPostThread" export * from "./core/StepPostUnlike" +export * from "./core/StepPostUnrepost" // Single lists export * from "./core/StepList" From d884be35439d10154ae3d840faa76fb3bf5e92d6 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:05:19 +0000 Subject: [PATCH 08/27] feat: implement StepDeletePost --- docs/guide/features.md | 2 +- lib/core/StepDeletePost.ts | 51 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 11 +++++++- lib/trotsky.ts | 1 + 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepDeletePost.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 3001c87..38fd295 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -44,6 +44,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostThread** | :white_check_mark: | Get a full post thread with replies. | ```Trotsky.init(agent).post("at://...").thread()``` **StepPostUnlike** | :white_check_mark: | Unlike a post. | ```Trotsky.init(agent).post("at://...").unlike()``` **StepPostUnrepost** | :white_check_mark: | Unrepost a post. | ```Trotsky.init(agent).post("at://...").unrepost()``` + **StepDeletePost** | :white_check_mark: | Delete a post. | ```Trotsky.init(agent).post("at://...").delete()``` ## Planned Features @@ -55,7 +56,6 @@ The following features are planned for future implementation: **StepActorKnownFollowers** | :construction: | Get known followers (mutual connections). | `app.bsky.graph.getKnownFollowers` **StepActorMutes** | :construction: | Get all actors muted by the user. | `app.bsky.graph.getMutes` **StepActorSuggestions** | :construction: | Get suggested actors to follow. | `app.bsky.actor.getSuggestions` - **StepDeletePost** | :construction: | Delete a post. | Delete post record **StepFeed** | :construction: | Get posts from a custom feed. | `app.bsky.feed.getFeed` **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` diff --git a/lib/core/StepDeletePost.ts b/lib/core/StepDeletePost.ts new file mode 100644 index 0000000..eb8e257 --- /dev/null +++ b/lib/core/StepDeletePost.ts @@ -0,0 +1,51 @@ +import { Step, type StepPost, type StepPostOutput } from "../trotsky" + +/** + * Represents a step for deleting a specific post. + * + * @typeParam P - The parent type of this step, defaulting to {@link StepPost}. + * @typeParam C - The context type, defaulting to {@link StepPostOutput}. + * @typeParam O - The output type, defaulting to `null`. + * + * @example + * Delete a specific post: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l") + * .delete() + * .run() + * ``` + * + * @example + * Delete posts matching criteria: + * ```ts + * await Trotsky.init(agent) + * .actor("myhandle.bsky.social") + * .posts() + * .take(10) + * .each() + * .when((step) => step?.context?.record?.text?.includes("#delete")) + * .delete() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepDeletePost

extends Step { + + /** + * Applies the step logic to delete the post provided in the context. + * + * @override + * @throws Error if the context does not provide the required post data. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepDeletePost") + } + + const { uri } = this.context + await this.agent.deletePost(uri) + } +} diff --git a/lib/core/mixins/PostMixins.ts b/lib/core/mixins/PostMixins.ts index fbca404..9cd6b7c 100644 --- a/lib/core/mixins/PostMixins.ts +++ b/lib/core/mixins/PostMixins.ts @@ -1,7 +1,7 @@ import type { AppBskyFeedPost } from "@atproto/api" import type { Resolvable } from "../utils/resolvable" -import { Step, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, StepPostUnlike, StepPostUnrepost, type StepPostThreadQueryParams } from "../../trotsky" +import { Step, StepDeletePost, StepPostAuthor, StepPostLike, StepPostLikers, StepPostQuotes, StepPostReply, StepPostRepost, StepPostReposters, StepPostThread, StepPostUnlike, StepPostUnrepost, type StepPostThreadQueryParams } from "../../trotsky" /** * Type representing the parameters for a post reply, including text and optional additional metadata. @@ -117,4 +117,13 @@ export abstract class PostMixins extends Step { thread (options?: Partial): StepPostThread { return this.append(StepPostThread, options) } + + /** + * Appends a step to delete the current post. + * + * @returns The appended {@link StepDeletePost} instance. + */ + delete (): StepDeletePost { + return this.append(StepDeletePost) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 53fba6d..d637dea 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -90,6 +90,7 @@ export * from "./core/StepActorUnmute" // Single post export * from "./core/StepCreatePost" +export * from "./core/StepDeletePost" export * from "./core/StepPost" export * from "./core/StepPostAuthor" export * from "./core/StepPostLike" From 4de416ad1b5394b0f282f674ac961c51e766fe27 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:07:17 +0000 Subject: [PATCH 09/27] feat: implement StepActorBlocks --- docs/guide/features.md | 2 +- lib/core/StepActorBlocks.ts | 76 +++++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 9 +++++ lib/trotsky.ts | 1 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepActorBlocks.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 38fd295..5e919d9 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -45,6 +45,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostUnlike** | :white_check_mark: | Unlike a post. | ```Trotsky.init(agent).post("at://...").unlike()``` **StepPostUnrepost** | :white_check_mark: | Unrepost a post. | ```Trotsky.init(agent).post("at://...").unrepost()``` **StepDeletePost** | :white_check_mark: | Delete a post. | ```Trotsky.init(agent).post("at://...").delete()``` + **StepActorBlocks** | :white_check_mark: | Get all actors blocked by the user. | ```Trotsky.init(agent).blocks().each()``` ## Planned Features @@ -52,7 +53,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepActorBlocks** | :construction: | Get all actors blocked by the user. | `app.bsky.graph.getBlocks` **StepActorKnownFollowers** | :construction: | Get known followers (mutual connections). | `app.bsky.graph.getKnownFollowers` **StepActorMutes** | :construction: | Get all actors muted by the user. | `app.bsky.graph.getMutes` **StepActorSuggestions** | :construction: | Get suggested actors to follow. | `app.bsky.actor.getSuggestions` diff --git a/lib/core/StepActorBlocks.ts b/lib/core/StepActorBlocks.ts new file mode 100644 index 0000000..41ecd7e --- /dev/null +++ b/lib/core/StepActorBlocks.ts @@ -0,0 +1,76 @@ +import type { AppBskyGraphGetBlocks } from "@atproto/api" + +import { StepActors, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the blocked actors retrieved by {@link StepActorBlocks}. + * @public + */ +export type StepActorBlocksOutput = AppBskyGraphGetBlocks.OutputSchema["blocks"] + +/** + * Type representing the query parameters for retrieving blocked actors. + * @public + */ +export type StepActorBlocksQueryParams = AppBskyGraphGetBlocks.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepActorBlocksQueryParamsCursor = StepActorBlocksQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving the authenticated user's blocked actors using the Bluesky API. + * Supports paginated retrieval of blocked accounts. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepActorBlocksOutput}. + * + * @example + * Get and iterate through blocked actors: + * ```ts + * await Trotsky.init(agent) + * .blocks() + * .each() + * .tap((step) => { + * console.log(`Blocked: ${step.context.handle}`) + * }) + * .run() + * ``` + * + * @example + * Unblock all blocked actors: + * ```ts + * await Trotsky.init(agent) + * .blocks() + * .each() + * .unblock() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepActorBlocks

extends StepActors { + + /** + * Applies pagination to retrieve blocked actors and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("blocks", (cursor) => { + return this.agent.app.bsky.graph.getBlocks(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving blocked actors, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving blocked actors. + */ + private queryParams (cursor: StepActorBlocksQueryParamsCursor): StepActorBlocksQueryParams { + return { cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 9884e14..58a7be1 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -16,6 +16,7 @@ import type { StepSearchActorsQueryParams } from "./StepSearchActors" import { StepActor, + StepActorBlocks, StepActors, StepWait, StepPost, @@ -65,6 +66,14 @@ export class Trotsky extends StepBuilder { return this.append(StepActors, param) } + /** + * Adds a {@link StepActorBlocks} step. + * @returns The new {@link StepActorBlocks} instance. + */ + blocks (): StepActorBlocks { + return this.append(StepActorBlocks) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index d637dea..caecc02 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -75,6 +75,7 @@ export * from "./core/StepListsEntry" // Single actor export * from "./core/StepActor" export * from "./core/StepActorBlock" +export * from "./core/StepActorBlocks" export * from "./core/StepActorFollow" export * from "./core/StepActorFollowers" export * from "./core/StepActorFollowings" From 286fbefffb3f432192cd6d720ea2515a4a76b039 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:09:03 +0000 Subject: [PATCH 10/27] feat: implement StepActorMutes --- docs/guide/features.md | 2 +- lib/core/StepActorMutes.ts | 76 ++++++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 9 +++++ lib/trotsky.ts | 1 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepActorMutes.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 5e919d9..3ac4391 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -46,6 +46,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepPostUnrepost** | :white_check_mark: | Unrepost a post. | ```Trotsky.init(agent).post("at://...").unrepost()``` **StepDeletePost** | :white_check_mark: | Delete a post. | ```Trotsky.init(agent).post("at://...").delete()``` **StepActorBlocks** | :white_check_mark: | Get all actors blocked by the user. | ```Trotsky.init(agent).blocks().each()``` + **StepActorMutes** | :white_check_mark: | Get all actors muted by the user. | ```Trotsky.init(agent).mutes().each()``` ## Planned Features @@ -54,7 +55,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- **StepActorKnownFollowers** | :construction: | Get known followers (mutual connections). | `app.bsky.graph.getKnownFollowers` - **StepActorMutes** | :construction: | Get all actors muted by the user. | `app.bsky.graph.getMutes` **StepActorSuggestions** | :construction: | Get suggested actors to follow. | `app.bsky.actor.getSuggestions` **StepFeed** | :construction: | Get posts from a custom feed. | `app.bsky.feed.getFeed` **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` diff --git a/lib/core/StepActorMutes.ts b/lib/core/StepActorMutes.ts new file mode 100644 index 0000000..6e1dda4 --- /dev/null +++ b/lib/core/StepActorMutes.ts @@ -0,0 +1,76 @@ +import type { AppBskyGraphGetMutes } from "@atproto/api" + +import { StepActors, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the muted actors retrieved by {@link StepActorMutes}. + * @public + */ +export type StepActorMutesOutput = AppBskyGraphGetMutes.OutputSchema["mutes"] + +/** + * Type representing the query parameters for retrieving muted actors. + * @public + */ +export type StepActorMutesQueryParams = AppBskyGraphGetMutes.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepActorMutesQueryParamsCursor = StepActorMutesQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving the authenticated user's muted actors using the Bluesky API. + * Supports paginated retrieval of muted accounts. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepActorMutesOutput}. + * + * @example + * Get and iterate through muted actors: + * ```ts + * await Trotsky.init(agent) + * .mutes() + * .each() + * .tap((step) => { + * console.log(`Muted: ${step.context.handle}`) + * }) + * .run() + * ``` + * + * @example + * Unmute all muted actors: + * ```ts + * await Trotsky.init(agent) + * .mutes() + * .each() + * .unmute() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepActorMutes

extends StepActors { + + /** + * Applies pagination to retrieve muted actors and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("mutes", (cursor) => { + return this.agent.app.bsky.graph.getMutes(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving muted actors, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving muted actors. + */ + private queryParams (cursor: StepActorMutesQueryParamsCursor): StepActorMutesQueryParams { + return { cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 58a7be1..4a17bb3 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -17,6 +17,7 @@ import type { StepSearchActorsQueryParams } from "./StepSearchActors" import { StepActor, StepActorBlocks, + StepActorMutes, StepActors, StepWait, StepPost, @@ -74,6 +75,14 @@ export class Trotsky extends StepBuilder { return this.append(StepActorBlocks) } + /** + * Adds a {@link StepActorMutes} step. + * @returns The new {@link StepActorMutes} instance. + */ + mutes (): StepActorMutes { + return this.append(StepActorMutes) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index caecc02..00bc596 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -77,6 +77,7 @@ export * from "./core/StepActor" export * from "./core/StepActorBlock" export * from "./core/StepActorBlocks" export * from "./core/StepActorFollow" +export * from "./core/StepActorMutes" export * from "./core/StepActorFollowers" export * from "./core/StepActorFollowings" export * from "./core/StepActorLikes" From 9462b06b911cb358147a4b98bc50fce57c927fc0 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:10:50 +0000 Subject: [PATCH 11/27] feat: implement StepActorKnownFollowers --- docs/guide/features.md | 2 +- lib/core/StepActorKnownFollowers.ts | 89 +++++++++++++++++++++++++++++ lib/core/mixins/ActorMixins.ts | 12 +++- lib/trotsky.ts | 1 + 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepActorKnownFollowers.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 3ac4391..49863e0 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -47,6 +47,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepDeletePost** | :white_check_mark: | Delete a post. | ```Trotsky.init(agent).post("at://...").delete()``` **StepActorBlocks** | :white_check_mark: | Get all actors blocked by the user. | ```Trotsky.init(agent).blocks().each()``` **StepActorMutes** | :white_check_mark: | Get all actors muted by the user. | ```Trotsky.init(agent).mutes().each()``` + **StepActorKnownFollowers** | :white_check_mark: | Get known followers (mutual connections). | ```Trotsky.init(agent).actor('bsky.app').knownFollowers().each()``` ## Planned Features @@ -54,7 +55,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepActorKnownFollowers** | :construction: | Get known followers (mutual connections). | `app.bsky.graph.getKnownFollowers` **StepActorSuggestions** | :construction: | Get suggested actors to follow. | `app.bsky.actor.getSuggestions` **StepFeed** | :construction: | Get posts from a custom feed. | `app.bsky.feed.getFeed` **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` diff --git a/lib/core/StepActorKnownFollowers.ts b/lib/core/StepActorKnownFollowers.ts new file mode 100644 index 0000000..e373cd3 --- /dev/null +++ b/lib/core/StepActorKnownFollowers.ts @@ -0,0 +1,89 @@ +import type { AppBskyGraphGetKnownFollowers } from "@atproto/api" + +import { + StepActors, + type StepActor, + type StepActorOutput +} from "../trotsky" + +/** + * Type representing the output of the known followers retrieved by {@link StepActorKnownFollowers}. + * @public + */ +export type StepActorKnownFollowersOutput = AppBskyGraphGetKnownFollowers.OutputSchema["followers"] + +/** + * Type representing the query parameters for retrieving known followers. + * @public + */ +export type StepActorKnownFollowersQueryParams = AppBskyGraphGetKnownFollowers.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepActorKnownFollowersQueryParamsCursor = StepActorKnownFollowersQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving an actor's known followers (mutual connections) using the Bluesky API. + * Supports paginated retrieval of known followers. + * + * @typeParam P - Type of the parent step, extending {@link StepActor}. + * @typeParam C - Type of the context object, extending {@link StepActorOutput}. + * @typeParam O - Type of the output object, extending {@link StepActorKnownFollowersOutput}. + * + * @example + * Get and iterate through an actor's known followers: + * ```ts + * await Trotsky.init(agent) + * .actor("bsky.app") + * .knownFollowers() + * .each() + * .tap((step) => { + * console.log(`Known follower: ${step.context.handle}`) + * }) + * .run() + * ``` + * + * @example + * Find mutual connections and follow them: + * ```ts + * await Trotsky.init(agent) + * .actor("friend.bsky.social") + * .knownFollowers() + * .each() + * .when((step) => !step?.context?.viewer?.following) + * .follow() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepActorKnownFollowers

extends StepActors { + + /** + * Applies pagination to retrieve known followers and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("followers", (cursor) => { + return this.agent.app.bsky.graph.getKnownFollowers(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving known followers, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving known followers. + * @throws + * Error if no context is found. + */ + private queryParams (cursor: StepActorKnownFollowersQueryParamsCursor): StepActorKnownFollowersQueryParams { + if (!this.context) { + throw new Error("No context found for StepActorKnownFollowers") + } + + return { "actor": this.context.did, cursor } + } +} diff --git a/lib/core/mixins/ActorMixins.ts b/lib/core/mixins/ActorMixins.ts index cd1bf71..e2a6548 100644 --- a/lib/core/mixins/ActorMixins.ts +++ b/lib/core/mixins/ActorMixins.ts @@ -4,6 +4,7 @@ import { StepActorFollow, StepActorFollowers, StepActorFollowings, + StepActorKnownFollowers, StepActorLists, StepActorLikes, StepActorMute, @@ -38,13 +39,22 @@ export abstract class ActorMixins extends Step { /** * Appends a step to fetch the followings of the current actor. - * + * * @returns The appended {@link StepActorFollowings} instance. */ followings (): StepActorFollowings { return this.append(StepActorFollowings) } + /** + * Appends a step to fetch the known followers (mutual connections) of the current actor. + * + * @returns The appended {@link StepActorKnownFollowers} instance. + */ + knownFollowers (): StepActorKnownFollowers { + return this.append(StepActorKnownFollowers) + } + /** * Appends a step to fetch the liked posts of the current actor. * diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 00bc596..10fe1e0 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -80,6 +80,7 @@ export * from "./core/StepActorFollow" export * from "./core/StepActorMutes" export * from "./core/StepActorFollowers" export * from "./core/StepActorFollowings" +export * from "./core/StepActorKnownFollowers" export * from "./core/StepActorLikes" export * from "./core/StepActorLists" export * from "./core/StepActorMute" From 6f3f4e3388a1386c050760b2edc94ec0165a88df Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:12:26 +0000 Subject: [PATCH 12/27] feat: implement StepActorSuggestions --- docs/guide/features.md | 2 +- lib/core/StepActorSuggestions.ts | 77 ++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 9 ++++ lib/trotsky.ts | 1 + 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepActorSuggestions.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 49863e0..7c196c1 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -48,6 +48,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepActorBlocks** | :white_check_mark: | Get all actors blocked by the user. | ```Trotsky.init(agent).blocks().each()``` **StepActorMutes** | :white_check_mark: | Get all actors muted by the user. | ```Trotsky.init(agent).mutes().each()``` **StepActorKnownFollowers** | :white_check_mark: | Get known followers (mutual connections). | ```Trotsky.init(agent).actor('bsky.app').knownFollowers().each()``` + **StepActorSuggestions** | :white_check_mark: | Get suggested actors to follow. | ```Trotsky.init(agent).suggestions().each()``` ## Planned Features @@ -55,7 +56,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepActorSuggestions** | :construction: | Get suggested actors to follow. | `app.bsky.actor.getSuggestions` **StepFeed** | :construction: | Get posts from a custom feed. | `app.bsky.feed.getFeed` **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` diff --git a/lib/core/StepActorSuggestions.ts b/lib/core/StepActorSuggestions.ts new file mode 100644 index 0000000..0aa0db2 --- /dev/null +++ b/lib/core/StepActorSuggestions.ts @@ -0,0 +1,77 @@ +import type { AppBskyActorGetSuggestions } from "@atproto/api" + +import { StepActors, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the suggested actors retrieved by {@link StepActorSuggestions}. + * @public + */ +export type StepActorSuggestionsOutput = AppBskyActorGetSuggestions.OutputSchema["actors"] + +/** + * Type representing the query parameters for retrieving suggested actors. + * @public + */ +export type StepActorSuggestionsQueryParams = AppBskyActorGetSuggestions.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepActorSuggestionsQueryParamsCursor = StepActorSuggestionsQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving suggested actors to follow using the Bluesky API. + * Supports paginated retrieval of suggestions. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepActorSuggestionsOutput}. + * + * @example + * Get and iterate through suggested actors: + * ```ts + * await Trotsky.init(agent) + * .suggestions() + * .each() + * .tap((step) => { + * console.log(`Suggested: ${step.context.handle}`) + * }) + * .run() + * ``` + * + * @example + * Follow suggested actors: + * ```ts + * await Trotsky.init(agent) + * .suggestions() + * .take(10) + * .each() + * .follow() + * .wait(2000) + * .run() + * ``` + * + * @public + */ +export class StepActorSuggestions

extends StepActors { + + /** + * Applies pagination to retrieve suggested actors and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("actors", (cursor) => { + return this.agent.app.bsky.actor.getSuggestions(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving suggested actors, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving suggested actors. + */ + private queryParams (cursor: StepActorSuggestionsQueryParamsCursor): StepActorSuggestionsQueryParams { + return { cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 4a17bb3..0ae12e6 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -18,6 +18,7 @@ import { StepActor, StepActorBlocks, StepActorMutes, + StepActorSuggestions, StepActors, StepWait, StepPost, @@ -83,6 +84,14 @@ export class Trotsky extends StepBuilder { return this.append(StepActorMutes) } + /** + * Adds a {@link StepActorSuggestions} step. + * @returns The new {@link StepActorSuggestions} instance. + */ + suggestions (): StepActorSuggestions { + return this.append(StepActorSuggestions) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 10fe1e0..11cae5b 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -86,6 +86,7 @@ export * from "./core/StepActorLists" export * from "./core/StepActorMute" export * from "./core/StepActorPosts" export * from "./core/StepActorStarterPacks" +export * from "./core/StepActorSuggestions" export * from "./core/StepActorStreamPosts" export * from "./core/StepActorUnblock" export * from "./core/StepActorUnfollow" From 15a2e43b41382d2b34b38b8b7de373cdff6f91f2 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:14:19 +0000 Subject: [PATCH 13/27] feat: implement StepFeed --- docs/guide/features.md | 2 +- lib/core/StepFeed.ts | 112 +++++++++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 11 ++++ lib/trotsky.ts | 1 + 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepFeed.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 7c196c1..f8e8dbe 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -49,6 +49,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepActorMutes** | :white_check_mark: | Get all actors muted by the user. | ```Trotsky.init(agent).mutes().each()``` **StepActorKnownFollowers** | :white_check_mark: | Get known followers (mutual connections). | ```Trotsky.init(agent).actor('bsky.app').knownFollowers().each()``` **StepActorSuggestions** | :white_check_mark: | Get suggested actors to follow. | ```Trotsky.init(agent).suggestions().each()``` + **StepFeed** | :white_check_mark: | Get posts from a custom feed. | ```Trotsky.init(agent).feed("at://...").each()``` ## Planned Features @@ -56,7 +57,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepFeed** | :construction: | Get posts from a custom feed. | `app.bsky.feed.getFeed` **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` **StepListFeed** | :construction: | Get posts from a list feed. | `app.bsky.feed.getListFeed` diff --git a/lib/core/StepFeed.ts b/lib/core/StepFeed.ts new file mode 100644 index 0000000..695d4a4 --- /dev/null +++ b/lib/core/StepFeed.ts @@ -0,0 +1,112 @@ +import type { AppBskyFeedGetFeed, AppBskyFeedDefs, AtpAgent } from "@atproto/api" + +import { StepPosts, type StepPostsOutput, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the feed retrieved by {@link StepFeed}. + * @public + */ +export type StepFeedOutput = StepPostsOutput + +/** + * Type representing the URI of a custom feed. + * @public + */ +export type StepFeedUri = string + +/** + * Type representing the query parameters for retrieving a feed. + * @public + */ +export type StepFeedQueryParams = AppBskyFeedGetFeed.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepFeedQueryParamsCursor = StepFeedQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving posts from a custom feed using the Bluesky API. + * Supports paginated retrieval of posts. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object, defaulting to `null`. + * @typeParam O - Type of the output object, extending {@link StepFeedOutput}. + * + * @example + * Get posts from a custom feed: + * ```ts + * await Trotsky.init(agent) + * .feed("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot") + * .take(20) + * .each() + * .tap((step) => { + * console.log(`@${step.context.author.handle}: ${step.context.record.text}`) + * }) + * .run() + * ``` + * + * @example + * Like posts from a feed with specific criteria: + * ```ts + * await Trotsky.init(agent) + * .feed("at://did:plc:example/app.bsky.feed.generator/typescript") + * .take(50) + * .each() + * .when((step) => step?.context?.likeCount > 10) + * .like() + * .wait(1000) + * .run() + * ``` + * + * @public + */ +export class StepFeed

extends StepPosts { + + /** + * The URI of the custom feed. + */ + _feedUri: StepFeedUri + + /** + * Initializes the StepFeed instance with the given agent, parent, and feed URI. + * + * @param agent - The AT protocol agent used for API calls. + * @param parent - The parent step in the chain. + * @param feedUri - The URI of the custom feed. + */ + constructor (agent: AtpAgent, parent: P, feedUri: StepFeedUri) { + super(agent, parent) + this._feedUri = feedUri + } + + /** + * Clones the current step and returns a new instance with the same parameters. + * @returns A new {@link StepFeed} instance. + */ + override clone () { + return super.clone(this._feedUri) + } + + /** + * Applies pagination to retrieve feed posts and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + const feed = await this.paginate("feed", (cursor) => { + return this.agent.app.bsky.feed.getFeed(this.queryParams(cursor)) + }) + + this.output = feed.map((post: AppBskyFeedDefs.FeedViewPost) => post.post) as O + } + + /** + * Generates query parameters for retrieving the feed, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving the feed. + */ + queryParams (cursor: StepFeedQueryParamsCursor): StepFeedQueryParams { + return { "feed": this._feedUri, cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 0ae12e6..160a4b6 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -13,6 +13,7 @@ import type { StepStarterPackUri } from "./StepStarterPack" import type { StepStarterPacksUris } from "./StepStarterPacks" import type { StepSearchStarterPacksQueryParams } from "./StepSearchStarterPacks" import type { StepSearchActorsQueryParams } from "./StepSearchActors" +import type { StepFeedUri } from "./StepFeed" import { StepActor, @@ -23,6 +24,7 @@ import { StepWait, StepPost, StepCreatePost, + StepFeed, StepList, StepListUri, StepStarterPack, @@ -92,6 +94,15 @@ export class Trotsky extends StepBuilder { return this.append(StepActorSuggestions) } + /** + * Adds a {@link StepFeed} step. + * @param uri - The feed URI. + * @returns The new {@link StepFeed} instance. + */ + feed (uri: Resolvable): StepFeed { + return this.append(StepFeed, uri) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 11cae5b..52333b5 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -63,6 +63,7 @@ export * from "./core/StepActorsEntry" export * from "./core/StepSearchActors" // List of posts +export * from "./core/StepFeed" export * from "./core/StepPosts" export * from "./core/StepPostsEntry" export * from "./core/StepSearchPosts" From 20e36764d1b9b9c98b664a761056bebd92f8060a Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:16:00 +0000 Subject: [PATCH 14/27] feat: implement StepFeedGenerator --- docs/guide/features.md | 2 +- lib/core/StepFeedGenerator.ts | 75 +++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 11 +++++ lib/trotsky.ts | 1 + 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepFeedGenerator.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index f8e8dbe..2a5295c 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -50,6 +50,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepActorKnownFollowers** | :white_check_mark: | Get known followers (mutual connections). | ```Trotsky.init(agent).actor('bsky.app').knownFollowers().each()``` **StepActorSuggestions** | :white_check_mark: | Get suggested actors to follow. | ```Trotsky.init(agent).suggestions().each()``` **StepFeed** | :white_check_mark: | Get posts from a custom feed. | ```Trotsky.init(agent).feed("at://...").each()``` + **StepFeedGenerator** | :white_check_mark: | Get a custom feed generator. | ```Trotsky.init(agent).feedGenerator("at://...")``` ## Planned Features @@ -57,7 +58,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepFeedGenerator** | :construction: | Get a custom feed generator. | `app.bsky.feed.getFeedGenerator` **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` **StepListFeed** | :construction: | Get posts from a list feed. | `app.bsky.feed.getListFeed` **StepListMute** | :construction: | Mute a list. | `app.bsky.graph.muteActorList` diff --git a/lib/core/StepFeedGenerator.ts b/lib/core/StepFeedGenerator.ts new file mode 100644 index 0000000..0ab955a --- /dev/null +++ b/lib/core/StepFeedGenerator.ts @@ -0,0 +1,75 @@ +import type { AppBskyFeedGetFeedGenerator, AtpAgent } from "@atproto/api" + +import { Step, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the feed generator retrieved by {@link StepFeedGenerator}. + * @public + */ +export type StepFeedGeneratorOutput = AppBskyFeedGetFeedGenerator.OutputSchema + +/** + * Type representing the URI of a feed generator. + * @public + */ +export type StepFeedGeneratorUri = string + +/** + * Represents a step for retrieving information about a feed generator using the Bluesky API. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object, defaulting to `null`. + * @typeParam O - Type of the output object, extending {@link StepFeedGeneratorOutput}. + * + * @example + * Get feed generator information: + * ```ts + * await Trotsky.init(agent) + * .feedGenerator("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot") + * .tap((step) => { + * console.log(`Feed: ${step.output.view.displayName}`) + * console.log(`Likes: ${step.output.view.likeCount}`) + * }) + * .run() + * ``` + * + * @public + */ +export class StepFeedGenerator

extends Step { + + /** + * The URI of the feed generator. + */ + _feedUri: StepFeedGeneratorUri + + /** + * Initializes the StepFeedGenerator instance with the given agent, parent, and feed URI. + * + * @param agent - The AT protocol agent used for API calls. + * @param parent - The parent step in the chain. + * @param feedUri - The URI of the feed generator. + */ + constructor (agent: AtpAgent, parent: P, feedUri: StepFeedGeneratorUri) { + super(agent, parent) + this._feedUri = feedUri + } + + /** + * Clones the current step and returns a new instance with the same parameters. + * @returns A new {@link StepFeedGenerator} instance. + */ + override clone () { + return super.clone(this._feedUri) + } + + /** + * Applies the step logic to retrieve the feed generator information. + */ + async apply () { + const { data } = await this.agent.app.bsky.feed.getFeedGenerator({ + "feed": this._feedUri + }) + + this.output = data as O + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 160a4b6..b0b967e 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -14,6 +14,7 @@ import type { StepStarterPacksUris } from "./StepStarterPacks" import type { StepSearchStarterPacksQueryParams } from "./StepSearchStarterPacks" import type { StepSearchActorsQueryParams } from "./StepSearchActors" import type { StepFeedUri } from "./StepFeed" +import type { StepFeedGeneratorUri } from "./StepFeedGenerator" import { StepActor, @@ -25,6 +26,7 @@ import { StepPost, StepCreatePost, StepFeed, + StepFeedGenerator, StepList, StepListUri, StepStarterPack, @@ -103,6 +105,15 @@ export class Trotsky extends StepBuilder { return this.append(StepFeed, uri) } + /** + * Adds a {@link StepFeedGenerator} step. + * @param uri - The feed generator URI. + * @returns The new {@link StepFeedGenerator} instance. + */ + feedGenerator (uri: Resolvable): StepFeedGenerator { + return this.append(StepFeedGenerator, uri) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 52333b5..5ad8c3f 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -64,6 +64,7 @@ export * from "./core/StepSearchActors" // List of posts export * from "./core/StepFeed" +export * from "./core/StepFeedGenerator" export * from "./core/StepPosts" export * from "./core/StepPostsEntry" export * from "./core/StepSearchPosts" From 45b1dd07b31cfdcfe18af890e63c8af2d13fd2a5 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:17:48 +0000 Subject: [PATCH 15/27] feat: implement StepSuggestedFeeds --- docs/guide/features.md | 2 +- lib/core/StepSuggestedFeeds.ts | 67 ++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 9 +++++ lib/trotsky.ts | 1 + 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepSuggestedFeeds.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 2a5295c..4665d56 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -51,6 +51,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepActorSuggestions** | :white_check_mark: | Get suggested actors to follow. | ```Trotsky.init(agent).suggestions().each()``` **StepFeed** | :white_check_mark: | Get posts from a custom feed. | ```Trotsky.init(agent).feed("at://...").each()``` **StepFeedGenerator** | :white_check_mark: | Get a custom feed generator. | ```Trotsky.init(agent).feedGenerator("at://...")``` + **StepSuggestedFeeds** | :white_check_mark: | Get suggested custom feeds. | ```Trotsky.init(agent).suggestedFeeds().take(10)``` ## Planned Features @@ -66,7 +67,6 @@ The following features are planned for future implementation: **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` - **StepSuggestedFeeds** | :construction: | Get suggested custom feeds. | `app.bsky.feed.getSuggestedFeeds` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` **StepThreadUnmute** | :construction: | Unmute a thread. | `app.bsky.graph.unmuteThread` diff --git a/lib/core/StepSuggestedFeeds.ts b/lib/core/StepSuggestedFeeds.ts new file mode 100644 index 0000000..2d7750f --- /dev/null +++ b/lib/core/StepSuggestedFeeds.ts @@ -0,0 +1,67 @@ +import type { AppBskyFeedGetSuggestedFeeds, AppBskyFeedDefs } from "@atproto/api" + +import { StepBuilderList, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the suggested feeds retrieved by {@link StepSuggestedFeeds}. + * @public + */ +export type StepSuggestedFeedsOutput = AppBskyFeedDefs.GeneratorView[] + +/** + * Type representing the query parameters for retrieving suggested feeds. + * @public + */ +export type StepSuggestedFeedsQueryParams = AppBskyFeedGetSuggestedFeeds.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepSuggestedFeedsQueryParamsCursor = StepSuggestedFeedsQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving suggested custom feeds using the Bluesky API. + * Supports paginated retrieval of feed suggestions. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepSuggestedFeedsOutput}. + * + * @example + * Get and iterate through suggested feeds: + * ```ts + * await Trotsky.init(agent) + * .suggestedFeeds() + * .take(10) + * .tap((step) => { + * step.output.forEach((feed) => { + * console.log(`Feed: ${feed.displayName} - ${feed.description}`) + * }) + * }) + * .run() + * ``` + * + * @public + */ +export class StepSuggestedFeeds

extends StepBuilderList { + + /** + * Applies pagination to retrieve suggested feeds and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("feeds", (cursor) => { + return this.agent.app.bsky.feed.getSuggestedFeeds(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving suggested feeds, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving suggested feeds. + */ + private queryParams (cursor: StepSuggestedFeedsQueryParamsCursor): StepSuggestedFeedsQueryParams { + return { cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index b0b967e..1a4941a 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -27,6 +27,7 @@ import { StepCreatePost, StepFeed, StepFeedGenerator, + StepSuggestedFeeds, StepList, StepListUri, StepStarterPack, @@ -114,6 +115,14 @@ export class Trotsky extends StepBuilder { return this.append(StepFeedGenerator, uri) } + /** + * Adds a {@link StepSuggestedFeeds} step. + * @returns The new {@link StepSuggestedFeeds} instance. + */ + suggestedFeeds (): StepSuggestedFeeds { + return this.append(StepSuggestedFeeds) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 5ad8c3f..bb082b0 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -66,6 +66,7 @@ export * from "./core/StepSearchActors" export * from "./core/StepFeed" export * from "./core/StepFeedGenerator" export * from "./core/StepPosts" +export * from "./core/StepSuggestedFeeds" export * from "./core/StepPostsEntry" export * from "./core/StepSearchPosts" export * from "./core/StepTimeline" From c06231d0c7ad0b7b0298e144905b2704d69f9bb0 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:19:31 +0000 Subject: [PATCH 16/27] feat: implement StepListFeed --- docs/guide/features.md | 2 +- lib/core/StepListFeed.ts | 88 +++++++++++++++++++++++++++++++++++ lib/core/mixins/ListMixins.ts | 19 ++++++-- lib/trotsky.ts | 1 + 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 lib/core/StepListFeed.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 4665d56..1fa30fa 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -52,6 +52,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepFeed** | :white_check_mark: | Get posts from a custom feed. | ```Trotsky.init(agent).feed("at://...").each()``` **StepFeedGenerator** | :white_check_mark: | Get a custom feed generator. | ```Trotsky.init(agent).feedGenerator("at://...")``` **StepSuggestedFeeds** | :white_check_mark: | Get suggested custom feeds. | ```Trotsky.init(agent).suggestedFeeds().take(10)``` + **StepListFeed** | :white_check_mark: | Get posts from a list feed. | ```Trotsky.init(agent).list("at://...").feed().each()``` ## Planned Features @@ -60,7 +61,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` - **StepListFeed** | :construction: | Get posts from a list feed. | `app.bsky.feed.getListFeed` **StepListMute** | :construction: | Mute a list. | `app.bsky.graph.muteActorList` **StepListUnblock** | :construction: | Unblock a list. | Delete listblock record **StepListUnmute** | :construction: | Unmute a list. | `app.bsky.graph.unmuteActorList` diff --git a/lib/core/StepListFeed.ts b/lib/core/StepListFeed.ts new file mode 100644 index 0000000..2fee6d5 --- /dev/null +++ b/lib/core/StepListFeed.ts @@ -0,0 +1,88 @@ +import type { AppBskyFeedGetListFeed, AppBskyFeedDefs } from "@atproto/api" + +import { StepPosts, type StepPostsOutput, type StepList, type StepListOutput } from "../trotsky" + +/** + * Type representing the output of the list feed retrieved by {@link StepListFeed}. + * @public + */ +export type StepListFeedOutput = StepPostsOutput + +/** + * Type representing the query parameters for retrieving a list feed. + * @public + */ +export type StepListFeedQueryParams = AppBskyFeedGetListFeed.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepListFeedQueryParamsCursor = StepListFeedQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving posts from a list feed using the Bluesky API. + * Supports paginated retrieval of posts. + * + * @typeParam P - Type of the parent step, extending {@link StepList}. + * @typeParam C - Type of the context object, extending {@link StepListOutput}. + * @typeParam O - Type of the output object, extending {@link StepListFeedOutput}. + * + * @example + * Get posts from a list feed: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .feed() + * .take(20) + * .each() + * .tap((step) => { + * console.log(`@${step.context.author.handle}: ${step.context.record.text}`) + * }) + * .run() + * ``` + * + * @example + * Like posts from a list feed: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .feed() + * .take(50) + * .each() + * .when((step) => step?.context?.likeCount > 10) + * .like() + * .wait(1000) + * .run() + * ``` + * + * @public + */ +export class StepListFeed

extends StepPosts { + + /** + * Applies pagination to retrieve list feed posts and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + const feed = await this.paginate("feed", (cursor) => { + return this.agent.app.bsky.feed.getListFeed(this.queryParams(cursor)) + }) + + this.output = feed.map((post: AppBskyFeedDefs.FeedViewPost) => post.post) as O + } + + /** + * Generates query parameters for retrieving the list feed, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving the list feed. + * @throws Error if no context is found. + */ + private queryParams (cursor: StepListFeedQueryParamsCursor): StepListFeedQueryParams { + if (!this.context) { + throw new Error("No context found for StepListFeed") + } + + return { "list": this.context.uri, cursor } + } +} diff --git a/lib/core/mixins/ListMixins.ts b/lib/core/mixins/ListMixins.ts index f7eeb84..350d440 100644 --- a/lib/core/mixins/ListMixins.ts +++ b/lib/core/mixins/ListMixins.ts @@ -1,23 +1,32 @@ -import { Step, StepListMembers } from "../../trotsky" +import { Step, StepListFeed, StepListMembers } from "../../trotsky" /** * A mixin class providing reusable list-related methods to extend step functionality. * These methods append specific list-related steps to the current chain. - * + * * @typeParam P - The parent step type. * @typeParam C - The context type. * @typeParam O - The output type. - * + * * @public */ export abstract class ListMixins extends Step { - + /** * Appends a step to fetch the members of the current list. - * + * * @returns The appended {@link StepListMembers} instance. */ members () { return this.append(StepListMembers) } + + /** + * Appends a step to fetch the posts from the current list's feed. + * + * @returns The appended {@link StepListFeed} instance. + */ + feed (): StepListFeed { + return this.append(StepListFeed) + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index bb082b0..b13a54e 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -112,6 +112,7 @@ export * from "./core/StepPostUnrepost" // Single lists export * from "./core/StepList" +export * from "./core/StepListFeed" export * from "./core/StepListMembers" // Single starter pack From ee287d19f31a7dc0924532513d1c5018b204e23d Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:21:06 +0000 Subject: [PATCH 17/27] feat: implement StepListBlock --- docs/guide/features.md | 2 +- lib/core/StepListBlock.ts | 39 +++++++++++++++++++++++++++++++++++ lib/core/mixins/ListMixins.ts | 12 ++++++++++- lib/trotsky.ts | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepListBlock.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 1fa30fa..edb68bc 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -53,6 +53,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepFeedGenerator** | :white_check_mark: | Get a custom feed generator. | ```Trotsky.init(agent).feedGenerator("at://...")``` **StepSuggestedFeeds** | :white_check_mark: | Get suggested custom feeds. | ```Trotsky.init(agent).suggestedFeeds().take(10)``` **StepListFeed** | :white_check_mark: | Get posts from a list feed. | ```Trotsky.init(agent).list("at://...").feed().each()``` + **StepListBlock** | :white_check_mark: | Block a list. | ```Trotsky.init(agent).list("at://...").block()``` ## Planned Features @@ -60,7 +61,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepListBlock** | :construction: | Block a list. | `app.bsky.graph.listblock` **StepListMute** | :construction: | Mute a list. | `app.bsky.graph.muteActorList` **StepListUnblock** | :construction: | Unblock a list. | Delete listblock record **StepListUnmute** | :construction: | Unmute a list. | `app.bsky.graph.unmuteActorList` diff --git a/lib/core/StepListBlock.ts b/lib/core/StepListBlock.ts new file mode 100644 index 0000000..a7a7d80 --- /dev/null +++ b/lib/core/StepListBlock.ts @@ -0,0 +1,39 @@ +import { Step, type StepList, type StepListOutput } from "../trotsky" + +/** + * Represents a step that performs a list block operation using the Bluesky API. + * Blocking a list prevents content from users on that list from appearing. + * + * @typeParam P - Type of the parent step, extending {@link StepList}. + * @typeParam C - Type of the context object, extending {@link StepListOutput}. + * @typeParam O - Type of the output object. + * + * @example + * Block a specific list: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .block() + * .run() + * ``` + * + * @public + */ +export class StepListBlock

extends Step { + + /** + * Applies the step by performing a list block operation. + * Requires the context to provide the `uri` of the list to block. + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepListBlock") + } + + const repo = this.agent.did! + const subject = this.context.uri + const createdAt = new Date().toISOString() + await this.agent.app.bsky.graph.listblock.create({ repo }, { subject, createdAt }) + } +} diff --git a/lib/core/mixins/ListMixins.ts b/lib/core/mixins/ListMixins.ts index 350d440..a979cbd 100644 --- a/lib/core/mixins/ListMixins.ts +++ b/lib/core/mixins/ListMixins.ts @@ -1,4 +1,4 @@ -import { Step, StepListFeed, StepListMembers } from "../../trotsky" +import { Step, StepListBlock, StepListFeed, StepListMembers } from "../../trotsky" /** * A mixin class providing reusable list-related methods to extend step functionality. @@ -29,4 +29,14 @@ export abstract class ListMixins extends Step { feed (): StepListFeed { return this.append(StepListFeed) } + + /** + * Appends a step to block the current list. + * + * @returns The current instance for method chaining. + */ + block () { + this.append(StepListBlock) + return this + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index b13a54e..1f6a313 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -112,6 +112,7 @@ export * from "./core/StepPostUnrepost" // Single lists export * from "./core/StepList" +export * from "./core/StepListBlock" export * from "./core/StepListFeed" export * from "./core/StepListMembers" From c35b4b0bca13504deeb828bdf7c5d28efdfa3364 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:23:02 +0000 Subject: [PATCH 18/27] feat: implement StepListUnblock --- docs/guide/features.md | 2 +- lib/core/StepListUnblock.ts | 47 +++++++++++++++++++++++++++++++++++ lib/core/mixins/ListMixins.ts | 12 ++++++++- lib/trotsky.ts | 1 + 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepListUnblock.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index edb68bc..dbab5fe 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -54,6 +54,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepSuggestedFeeds** | :white_check_mark: | Get suggested custom feeds. | ```Trotsky.init(agent).suggestedFeeds().take(10)``` **StepListFeed** | :white_check_mark: | Get posts from a list feed. | ```Trotsky.init(agent).list("at://...").feed().each()``` **StepListBlock** | :white_check_mark: | Block a list. | ```Trotsky.init(agent).list("at://...").block()``` + **StepListUnblock** | :white_check_mark: | Unblock a list. | ```Trotsky.init(agent).list("at://...").unblock()``` ## Planned Features @@ -62,7 +63,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- **StepListMute** | :construction: | Mute a list. | `app.bsky.graph.muteActorList` - **StepListUnblock** | :construction: | Unblock a list. | Delete listblock record **StepListUnmute** | :construction: | Unmute a list. | `app.bsky.graph.unmuteActorList` **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` diff --git a/lib/core/StepListUnblock.ts b/lib/core/StepListUnblock.ts new file mode 100644 index 0000000..53ee680 --- /dev/null +++ b/lib/core/StepListUnblock.ts @@ -0,0 +1,47 @@ +import { AtUri } from "@atproto/api" + +import { Step, type StepList, type StepListOutput } from "../trotsky" + +/** + * Represents a step that performs a list unblock operation using the Bluesky API. + * Unblocking a list allows content from users on that list to appear again. + * + * @typeParam P - Type of the parent step, extending {@link StepList}. + * @typeParam C - Type of the context object, extending {@link StepListOutput}. + * @typeParam O - Type of the output object. + * + * @example + * Unblock a specific list: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .unblock() + * .run() + * ``` + * + * @public + */ +export class StepListUnblock

extends Step { + + /** + * Applies the step by performing a list unblock operation. + * Requires the context to provide the `viewer.blocked` URI. + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepListUnblock") + } + + // Check if the list is currently blocked + const blockUri = this.context.viewer?.blocked + if (!blockUri) { + // List is not blocked, nothing to do + return + } + + const repo = this.agent.did! + const { rkey } = new AtUri(blockUri) + await this.agent.app.bsky.graph.listblock.delete({ repo, rkey }) + } +} diff --git a/lib/core/mixins/ListMixins.ts b/lib/core/mixins/ListMixins.ts index a979cbd..30f3fa5 100644 --- a/lib/core/mixins/ListMixins.ts +++ b/lib/core/mixins/ListMixins.ts @@ -1,4 +1,4 @@ -import { Step, StepListBlock, StepListFeed, StepListMembers } from "../../trotsky" +import { Step, StepListBlock, StepListFeed, StepListMembers, StepListUnblock } from "../../trotsky" /** * A mixin class providing reusable list-related methods to extend step functionality. @@ -39,4 +39,14 @@ export abstract class ListMixins extends Step { this.append(StepListBlock) return this } + + /** + * Appends a step to unblock the current list. + * + * @returns The current instance for method chaining. + */ + unblock () { + this.append(StepListUnblock) + return this + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 1f6a313..121aaeb 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -115,6 +115,7 @@ export * from "./core/StepList" export * from "./core/StepListBlock" export * from "./core/StepListFeed" export * from "./core/StepListMembers" +export * from "./core/StepListUnblock" // Single starter pack export * from "./core/StepStarterPack" From e136d511019014df7c0f8e915ee59e1899eada19 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:24:45 +0000 Subject: [PATCH 19/27] feat: implement StepListMute --- lib/core/StepListMute.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/core/StepListMute.ts diff --git a/lib/core/StepListMute.ts b/lib/core/StepListMute.ts new file mode 100644 index 0000000..269c6ab --- /dev/null +++ b/lib/core/StepListMute.ts @@ -0,0 +1,36 @@ +import { Step, type StepList, type StepListOutput } from "../trotsky" + +/** + * Represents a step that performs a list mute operation using the Bluesky API. + * Muting a list hides content from users on that list without blocking them. + * + * @typeParam P - Type of the parent step, extending {@link StepList}. + * @typeParam C - Type of the context object, extending {@link StepListOutput}. + * @typeParam O - Type of the output object. + * + * @example + * Mute a specific list: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .mute() + * .run() + * ``` + * + * @public + */ +export class StepListMute

extends Step { + + /** + * Applies the step by performing a list mute operation. + * Requires the context to provide the `uri` of the list to mute. + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepListMute") + } + + await this.agent.app.bsky.graph.muteActorList({ "list": this.context.uri }) + } +} From def94a5a5876ea0d100b55431dd0bbe022649c27 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:24:48 +0000 Subject: [PATCH 20/27] feat: implement StepListUnmute --- docs/guide/features.md | 4 ++-- lib/core/StepListUnmute.ts | 36 +++++++++++++++++++++++++++++++++++ lib/core/mixins/ListMixins.ts | 22 ++++++++++++++++++++- lib/trotsky.ts | 2 ++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 lib/core/StepListUnmute.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index dbab5fe..6829790 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -55,6 +55,8 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepListFeed** | :white_check_mark: | Get posts from a list feed. | ```Trotsky.init(agent).list("at://...").feed().each()``` **StepListBlock** | :white_check_mark: | Block a list. | ```Trotsky.init(agent).list("at://...").block()``` **StepListUnblock** | :white_check_mark: | Unblock a list. | ```Trotsky.init(agent).list("at://...").unblock()``` + **StepListMute** | :white_check_mark: | Mute a list. | ```Trotsky.init(agent).list("at://...").mute()``` + **StepListUnmute** | :white_check_mark: | Unmute a list. | ```Trotsky.init(agent).list("at://...").unmute()``` ## Planned Features @@ -62,8 +64,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepListMute** | :construction: | Mute a list. | `app.bsky.graph.muteActorList` - **StepListUnmute** | :construction: | Unmute a list. | `app.bsky.graph.unmuteActorList` **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` diff --git a/lib/core/StepListUnmute.ts b/lib/core/StepListUnmute.ts new file mode 100644 index 0000000..7803d54 --- /dev/null +++ b/lib/core/StepListUnmute.ts @@ -0,0 +1,36 @@ +import { Step, type StepList, type StepListOutput } from "../trotsky" + +/** + * Represents a step that performs a list unmute operation using the Bluesky API. + * Unmuting a list allows content from users on that list to appear again. + * + * @typeParam P - Type of the parent step, extending {@link StepList}. + * @typeParam C - Type of the context object, extending {@link StepListOutput}. + * @typeParam O - Type of the output object. + * + * @example + * Unmute a specific list: + * ```ts + * await Trotsky.init(agent) + * .list("at://did:plc:example/app.bsky.graph.list/listid") + * .unmute() + * .run() + * ``` + * + * @public + */ +export class StepListUnmute

extends Step { + + /** + * Applies the step by performing a list unmute operation. + * Requires the context to provide the `uri` of the list to unmute. + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepListUnmute") + } + + await this.agent.app.bsky.graph.unmuteActorList({ "list": this.context.uri }) + } +} diff --git a/lib/core/mixins/ListMixins.ts b/lib/core/mixins/ListMixins.ts index 30f3fa5..f23adfa 100644 --- a/lib/core/mixins/ListMixins.ts +++ b/lib/core/mixins/ListMixins.ts @@ -1,4 +1,4 @@ -import { Step, StepListBlock, StepListFeed, StepListMembers, StepListUnblock } from "../../trotsky" +import { Step, StepListBlock, StepListFeed, StepListMembers, StepListMute, StepListUnblock, StepListUnmute } from "../../trotsky" /** * A mixin class providing reusable list-related methods to extend step functionality. @@ -49,4 +49,24 @@ export abstract class ListMixins extends Step { this.append(StepListUnblock) return this } + + /** + * Appends a step to mute the current list. + * + * @returns The current instance for method chaining. + */ + mute () { + this.append(StepListMute) + return this + } + + /** + * Appends a step to unmute the current list. + * + * @returns The current instance for method chaining. + */ + unmute () { + this.append(StepListUnmute) + return this + } } diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 121aaeb..25fca00 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -115,7 +115,9 @@ export * from "./core/StepList" export * from "./core/StepListBlock" export * from "./core/StepListFeed" export * from "./core/StepListMembers" +export * from "./core/StepListMute" export * from "./core/StepListUnblock" +export * from "./core/StepListUnmute" // Single starter pack export * from "./core/StepStarterPack" From d9473cc7182cd04f99eff6cc27c59d2f5ead7b70 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:26:25 +0000 Subject: [PATCH 21/27] feat: implement StepNotifications --- docs/guide/features.md | 2 +- lib/core/StepNotifications.ts | 67 +++++++++++++++++++++++++++++++++++ lib/core/Trotsky.ts | 9 +++++ lib/trotsky.ts | 3 ++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 lib/core/StepNotifications.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 6829790..94fd5cd 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -57,6 +57,7 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepListUnblock** | :white_check_mark: | Unblock a list. | ```Trotsky.init(agent).list("at://...").unblock()``` **StepListMute** | :white_check_mark: | Mute a list. | ```Trotsky.init(agent).list("at://...").mute()``` **StepListUnmute** | :white_check_mark: | Unmute a list. | ```Trotsky.init(agent).list("at://...").unmute()``` + **StepNotifications** | :white_check_mark: | Get user notifications. | ```Trotsky.init(agent).notifications().take(20)``` ## Planned Features @@ -64,7 +65,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepNotifications** | :construction: | Get user notifications. | `app.bsky.notification.listNotifications` **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` diff --git a/lib/core/StepNotifications.ts b/lib/core/StepNotifications.ts new file mode 100644 index 0000000..f317222 --- /dev/null +++ b/lib/core/StepNotifications.ts @@ -0,0 +1,67 @@ +import type { AppBskyNotificationListNotifications } from "@atproto/api" + +import { StepBuilderList, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the notifications retrieved by {@link StepNotifications}. + * @public + */ +export type StepNotificationsOutput = AppBskyNotificationListNotifications.Notification[] + +/** + * Type representing the query parameters for retrieving notifications. + * @public + */ +export type StepNotificationsQueryParams = AppBskyNotificationListNotifications.QueryParams + +/** + * Type representing the cursor for paginated queries. + * @public + */ +export type StepNotificationsQueryParamsCursor = StepNotificationsQueryParams["cursor"] | undefined + +/** + * Represents a step for retrieving user notifications using the Bluesky API. + * Supports paginated retrieval of notifications. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepNotificationsOutput}. + * + * @example + * Get recent notifications: + * ```ts + * await Trotsky.init(agent) + * .notifications() + * .take(20) + * .tap((step) => { + * step.output.forEach((notif) => { + * console.log(`${notif.reason}: from ${notif.author.handle}`) + * }) + * }) + * .run() + * ``` + * + * @public + */ +export class StepNotifications

extends StepBuilderList { + + /** + * Applies pagination to retrieve notifications and sets the output. + * Fetches paginated results using the agent and appends them to the output. + */ + async applyPagination () { + this.output = await this.paginate("notifications", (cursor) => { + return this.agent.app.bsky.notification.listNotifications(this.queryParams(cursor)) + }) + } + + /** + * Generates query parameters for retrieving notifications, including the optional cursor. + * @param cursor - The cursor for paginated queries. + * @returns The query parameters for retrieving notifications. + */ + private queryParams (cursor: StepNotificationsQueryParamsCursor): StepNotificationsQueryParams { + return { cursor } + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 1a4941a..48b8d77 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -27,6 +27,7 @@ import { StepCreatePost, StepFeed, StepFeedGenerator, + StepNotifications, StepSuggestedFeeds, StepList, StepListUri, @@ -123,6 +124,14 @@ export class Trotsky extends StepBuilder { return this.append(StepSuggestedFeeds) } + /** + * Adds a {@link StepNotifications} step. + * @returns The new {@link StepNotifications} instance. + */ + notifications (): StepNotifications { + return this.append(StepNotifications) + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 25fca00..36d9ab0 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -127,6 +127,9 @@ export * from "./core/StepStarterPacks" export * from "./core/StepStarterPacksEntry" export * from "./core/StepSearchStarterPacks" +// Notifications +export * from "./core/StepNotifications" + // Utils export * from "./core/StepTap" export * from "./core/StepWait" From e5baf5f1492fd7a7c973d2260b924096212b1504 Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:28:05 +0000 Subject: [PATCH 22/27] feat: implement StepNotificationsUnreadCount --- lib/core/StepNotificationsUnreadCount.ts | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/core/StepNotificationsUnreadCount.ts diff --git a/lib/core/StepNotificationsUnreadCount.ts b/lib/core/StepNotificationsUnreadCount.ts new file mode 100644 index 0000000..92f9fad --- /dev/null +++ b/lib/core/StepNotificationsUnreadCount.ts @@ -0,0 +1,40 @@ +import type { AppBskyNotificationGetUnreadCount } from "@atproto/api" + +import { Step, type Trotsky } from "../trotsky" + +/** + * Type representing the output of the unread count retrieved by {@link StepNotificationsUnreadCount}. + * @public + */ +export type StepNotificationsUnreadCountOutput = AppBskyNotificationGetUnreadCount.OutputSchema + +/** + * Represents a step for retrieving the unread notification count using the Bluesky API. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object, extending {@link StepNotificationsUnreadCountOutput}. + * + * @example + * Get unread notification count: + * ```ts + * await Trotsky.init(agent) + * .notificationsUnreadCount() + * .tap((step) => { + * console.log(`Unread notifications: ${step.output.count}`) + * }) + * .run() + * ``` + * + * @public + */ +export class StepNotificationsUnreadCount

extends Step { + + /** + * Applies the step logic to retrieve the unread notification count. + */ + async apply () { + const { data } = await this.agent.app.bsky.notification.getUnreadCount() + this.output = data as O + } +} From f5e72b57e78368335a35092676819ef3882760ce Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:28:08 +0000 Subject: [PATCH 23/27] feat: implement StepNotificationsUpdateSeen --- docs/guide/features.md | 4 ++-- lib/core/StepNotificationsUpdateSeen.ts | 29 +++++++++++++++++++++++++ lib/core/Trotsky.ts | 19 ++++++++++++++++ lib/trotsky.ts | 2 ++ 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 lib/core/StepNotificationsUpdateSeen.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index 94fd5cd..fda494b 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -58,6 +58,8 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepListMute** | :white_check_mark: | Mute a list. | ```Trotsky.init(agent).list("at://...").mute()``` **StepListUnmute** | :white_check_mark: | Unmute a list. | ```Trotsky.init(agent).list("at://...").unmute()``` **StepNotifications** | :white_check_mark: | Get user notifications. | ```Trotsky.init(agent).notifications().take(20)``` + **StepNotificationsUnreadCount** | :white_check_mark: | Get unread notification count. | ```Trotsky.init(agent).notificationsUnreadCount()``` + **StepNotificationsUpdateSeen** | :white_check_mark: | Mark notifications as seen. | ```Trotsky.init(agent).notificationsUpdateSeen()``` ## Planned Features @@ -65,8 +67,6 @@ The following features are planned for future implementation: **Name** | **Status** | **Description** | **Potential API** ---|---|---|--- - **StepNotificationsUnreadCount** | :construction: | Get unread notification count. | `app.bsky.notification.getUnreadCount` - **StepNotificationsUpdateSeen** | :construction: | Mark notifications as seen. | `app.bsky.notification.updateSeen` **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` **StepThreadUnmute** | :construction: | Unmute a thread. | `app.bsky.graph.unmuteThread` diff --git a/lib/core/StepNotificationsUpdateSeen.ts b/lib/core/StepNotificationsUpdateSeen.ts new file mode 100644 index 0000000..bbcaa26 --- /dev/null +++ b/lib/core/StepNotificationsUpdateSeen.ts @@ -0,0 +1,29 @@ +import { Step, type Trotsky } from "../trotsky" + +/** + * Represents a step for marking notifications as seen using the Bluesky API. + * + * @typeParam P - Type of the parent step, extending {@link Trotsky}. + * @typeParam C - Type of the context object. + * @typeParam O - Type of the output object. + * + * @example + * Mark all notifications as seen: + * ```ts + * await Trotsky.init(agent) + * .notificationsUpdateSeen() + * .run() + * ``` + * + * @public + */ +export class StepNotificationsUpdateSeen

extends Step { + + /** + * Applies the step logic to mark notifications as seen. + */ + async apply () { + const seenAt = new Date().toISOString() + await this.agent.app.bsky.notification.updateSeen({ seenAt }) + } +} diff --git a/lib/core/Trotsky.ts b/lib/core/Trotsky.ts index 48b8d77..c8ead25 100644 --- a/lib/core/Trotsky.ts +++ b/lib/core/Trotsky.ts @@ -28,6 +28,8 @@ import { StepFeed, StepFeedGenerator, StepNotifications, + StepNotificationsUnreadCount, + StepNotificationsUpdateSeen, StepSuggestedFeeds, StepList, StepListUri, @@ -132,6 +134,23 @@ export class Trotsky extends StepBuilder { return this.append(StepNotifications) } + /** + * Adds a {@link StepNotificationsUnreadCount} step. + * @returns The new {@link StepNotificationsUnreadCount} instance. + */ + notificationsUnreadCount (): StepNotificationsUnreadCount { + return this.append(StepNotificationsUnreadCount) + } + + /** + * Adds a {@link StepNotificationsUpdateSeen} step. + * @returns The current {@link Trotsky} instance. + */ + notificationsUpdateSeen () { + this.append(StepNotificationsUpdateSeen) + return this + } + /** * Adds a {@link StepPost} step. * @param uri - The post URI. diff --git a/lib/trotsky.ts b/lib/trotsky.ts index 36d9ab0..a551c1b 100644 --- a/lib/trotsky.ts +++ b/lib/trotsky.ts @@ -129,6 +129,8 @@ export * from "./core/StepSearchStarterPacks" // Notifications export * from "./core/StepNotifications" +export * from "./core/StepNotificationsUnreadCount" +export * from "./core/StepNotificationsUpdateSeen" // Utils export * from "./core/StepTap" From cb0f37f7defdf1a16431023f4412188251f1e35d Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:29:53 +0000 Subject: [PATCH 24/27] feat: implement StepThreadMute --- lib/core/StepThreadMute.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 lib/core/StepThreadMute.ts diff --git a/lib/core/StepThreadMute.ts b/lib/core/StepThreadMute.ts new file mode 100644 index 0000000..80396db --- /dev/null +++ b/lib/core/StepThreadMute.ts @@ -0,0 +1,37 @@ +import { Step, type StepPost, type StepPostOutput } from "../trotsky" + +/** + * Represents a step that performs a thread mute operation using the Bluesky API. + * Muting a thread hides notifications for that thread. + * + * @typeParam P - Type of the parent step, extending {@link StepPost}. + * @typeParam C - Type of the context object, extending {@link StepPostOutput}. + * @typeParam O - Type of the output object. + * + * @example + * Mute a specific thread: + * ```ts + * await Trotsky.init(agent) + * .post("at://did:plc:example/app.bsky.feed.post/postid") + * .threadMute() + * .run() + * ``` + * + * @public + */ +export class StepThreadMute

extends Step { + + /** + * Applies the step by performing a thread mute operation. + * Requires the context to provide the root post URI. + * @throws Error if no context is found. + */ + async apply () { + if (!this.context) { + throw new Error("No context found for StepThreadMute") + } + + // Use the post's URI to get the root of the thread + await this.agent.app.bsky.graph.muteThread({ "root": this.context.uri }) + } +} From 8bbb7c0bcd498e078461720bb733c86801501c6d Mon Sep 17 00:00:00 2001 From: Pierre Romera Date: Thu, 4 Dec 2025 19:29:56 +0000 Subject: [PATCH 25/27] feat: implement StepThreadUnmute --- docs/guide/features.md | 13 +++--------- lib/core/StepThreadUnmute.ts | 37 +++++++++++++++++++++++++++++++++++ lib/core/mixins/PostMixins.ts | 22 ++++++++++++++++++++- lib/trotsky.ts | 2 ++ 4 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 lib/core/StepThreadUnmute.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index fda494b..1863668 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -60,17 +60,10 @@ Trotsky provides comprehensive support for the Bluesky AT Protocol. Below is a l **StepNotifications** | :white_check_mark: | Get user notifications. | ```Trotsky.init(agent).notifications().take(20)``` **StepNotificationsUnreadCount** | :white_check_mark: | Get unread notification count. | ```Trotsky.init(agent).notificationsUnreadCount()``` **StepNotificationsUpdateSeen** | :white_check_mark: | Mark notifications as seen. | ```Trotsky.init(agent).notificationsUpdateSeen()``` + **StepThreadMute** | :white_check_mark: | Mute a thread. | ```Trotsky.init(agent).post("at://...").threadMute()``` + **StepThreadUnmute** | :white_check_mark: | Unmute a thread. | ```Trotsky.init(agent).post("at://...").threadUnmute()``` -## Planned Features - -The following features are planned for future implementation: - - **Name** | **Status** | **Description** | **Potential API** ----|---|---|--- - **StepThreadMute** | :construction: | Mute a thread. | `app.bsky.graph.muteThread` - **StepThreadUnmute** | :construction: | Unmute a thread. | `app.bsky.graph.unmuteThread` - - :white_check_mark: Implemented • :test_tube: Experimental • :x: In PR/Review • :construction: Planned + :white_check_mark: Implemented • :test_tube: Experimental