Social Actions
Follow, block, and mute users on Bluesky, with typed refs and undo support.
Domain Types
FollowRef
Returned by Bluesky.follow -- pass to unfollow or undoFollow to undo.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the follow record |
BlockRef
Returned by Bluesky.block -- pass to unblock or undoBlock to undo.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the block record |
LikeRef
Returned by Bluesky.like -- pass to unlike or undoLike to undo.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the like record |
RepostRef
Returned by Bluesky.repost -- pass to unrepost or undoRepost to undo.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the repost record |
ListBlockRef
Returned by Bluesky.blockModList -- pass to unblockModList to undo.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the list block record |
UndoResult
Discriminated union returned by undo* functions indicating whether the action was reversed.
Case |
Description |
|---|---|
|
The record was deleted successfully |
|
There was nothing to undo (e.g. the post was not liked) |
Only unlikePost and unrepostPost can return WasNotPresent (they check viewer state). The ref-based undoLike, undoRepost, undoFollow, and undoBlock always return Undone because deleteRecord is idempotent.
Functions
Following
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Follow a user |
|
|
|
Follow by handle or DID string (resolves automatically) |
|
|
|
Unfollow by ref |
|
|
|
Unfollow by ref, returning |
taskResult {
// Follow using a profile's DID
let! followRef = Bluesky.follow agent profile
// Or by handle string
let! followRef2 = Bluesky.followByHandle agent "alice.bsky.social"
// Undo
do! Bluesky.unfollow agent followRef
}
Blocking
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Block a user |
|
|
|
Block by handle or DID string (resolves automatically) |
|
|
|
Unblock by ref |
|
|
|
Unblock by ref, returning |
|
|
|
Block an entire moderation list |
|
|
|
Unblock a moderation list |
taskResult {
let! blockRef = Bluesky.block agent profile
let! blockRef2 = Bluesky.blockByHandle agent "spammer.example.com"
// Moderation list
let! listBlockRef = Bluesky.blockModList agent modListUri
do! Bluesky.unblockModList agent listBlockRef
}
Muting
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Mute an account |
|
|
|
Mute by handle or DID string |
|
|
|
Unmute an account |
|
|
|
Unmute by handle or DID string |
|
|
|
Mute all accounts on a moderation list |
|
|
|
Unmute a moderation list |
|
|
|
Mute a thread |
|
|
|
Unmute a thread |
taskResult {
do! Bluesky.muteUser agent profile
do! Bluesky.muteUserByHandle agent "noisy.bsky.social"
do! Bluesky.muteThread agent post
do! Bluesky.muteModList agent modListUri
}
Generic Undo
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Delete any ref type via SRTP |
taskResult {
let! likeRef = Bluesky.like agent post
let! followRef = Bluesky.follow agent profile
// Generic undo works with any ref type
let! _ = Bluesky.undo agent likeRef
let! _ = Bluesky.undo agent followRef
()
}
SRTP Polymorphism
Social action functions accept multiple types via SRTP:
follow,block,muteUser,unmuteUseracceptDid,ProfileSummary, orProfilemuteThread,unmuteThreadacceptTimelinePost,PostRef, orAtUriundoacceptsLikeRef,RepostRef,FollowRef,BlockRef, orListBlockRef
taskResult {
let! profile = Bluesky.getProfile agent aliceHandle
// Pass the Profile directly -- no need to extract .Did
let! followRef = Bluesky.follow agent profile
do! Bluesky.muteUser agent profile
// Pass a TimelinePost directly to mute its thread
let! page = Bluesky.getTimeline agent (Some 1L) None
do! Bluesky.muteThread agent page.Items.Head.Post
}
namespace FSharp
--------------------
namespace Microsoft.FSharp
module AtpAgent from FSharp.ATProto.Core
<summary> Functions for creating and authenticating <see cref="AtpAgent" /> instances. </summary>
--------------------
type AtpAgent = { HttpClient: HttpClient mutable BaseUrl: Uri mutable Session: AtpSession option ExtraHeaders: (string * string) list AuthenticateRequest: (HttpRequestMessage -> unit) option RefreshAuthentication: (unit -> Task<Result<unit,XrpcError>>) option OnSessionChanged: (unit -> unit) option }
<summary> Client agent for communicating with an AT Protocol Personal Data Server (PDS). Holds the HTTP client, base URL, optional authenticated session, and extra headers. </summary>
<remarks> Create an agent with <see cref="AtpAgent.create" /> or <see cref="AtpAgent.createWithClient" />, then authenticate with <see cref="AtpAgent.login" />. The agent's <see cref="Session" /> field is mutable: it is updated automatically on login and token refresh. </remarks>
<example><code> let agent = AtpAgent.create "https://bsky.social" let! session = AtpAgent.login "my-handle.bsky.social" "app-password-here" agent </code></example>
module Profile from FSharp.ATProto.Bluesky
--------------------
type Profile = { Did: Did Handle: Handle DisplayName: string Description: string Avatar: string option Banner: string option PostsCount: int64 FollowersCount: int64 FollowsCount: int64 IsFollowing: bool ... }
<summary> A full user profile with engagement counts and relationship state. Maps from <c>ProfileViewDetailed</c>. </summary>
module TimelinePost from FSharp.ATProto.Bluesky
--------------------
type TimelinePost = { Uri: AtUri Cid: Cid Author: ProfileSummary Text: string Facets: Facet list LikeCount: int64 RepostCount: int64 ReplyCount: int64 QuoteCount: int64 IndexedAt: DateTimeOffset ... }
<summary> A post with engagement counts and viewer state, used in feeds and timelines. Maps from <c>PostView</c>. </summary>
module AtUri from FSharp.ATProto.Syntax
<summary> Functions for creating, validating, and extracting data from <see cref="AtUri" /> values. </summary>
--------------------
type AtUri = private | AtUri of string override ToString: unit -> string
<summary> An AT-URI that identifies a resource in the AT Protocol network. AT-URIs use the scheme <c>at://</c> followed by an authority (DID or handle), an optional collection (NSID), and an optional record key. Format: <c>at://<authority>[/<collection>[/<rkey>]]</c>. Maximum length is 8192 characters. </summary>
<remarks> See the AT Protocol specification: https://atproto.com/specs/at-uri-scheme </remarks>
module Handle from FSharp.ATProto.Syntax
<summary> Functions for creating, validating, and extracting data from <see cref="Handle" /> values. </summary>
--------------------
type Handle = private | Handle of string override ToString: unit -> string
<summary> A handle (domain name) used as a human-readable identifier in the AT Protocol. Handles are DNS-based names (e.g. <c>my-handle.bsky.social</c>) that resolve to a <see cref="Did" />. They must be valid domain names with at least two segments and a maximum length of 253 characters. </summary>
<remarks> See the AT Protocol specification: https://atproto.com/specs/handle </remarks>
<summary> High-level convenience methods for common Bluesky operations: posting, replying, liking, reposting, following, blocking, uploading blobs, and deleting records. All methods require an authenticated <see cref="AtpAgent" />. </summary>
<summary> Follow a user. Accepts a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" /> directly. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="target">The user to follow — a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" />.</param>
<returns>A <see cref="FollowRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>FollowRef</c> to <see cref="unfollow" /> to undo.</returns>
<summary> Follow a user by handle string. The handle is resolved to a DID, then the follow is created. Also accepts a DID string directly (if it starts with <c>did:</c>, it is parsed as a DID). </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="identifier">A handle (e.g., <c>my-handle.bsky.social</c>) or DID string (e.g., <c>did:plc:abc123</c>).</param>
<returns>A <see cref="FollowRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>FollowRef</c> to <see cref="unfollow" /> to undo.</returns>
<remarks> For type-safe usage when you already have a <see cref="Did" />, use <see cref="follow" /> instead. </remarks>
<summary> Unfollow a user by deleting the follow record. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="followRef">The <see cref="FollowRef" /> returned by <see cref="follow" />.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Block a user. Accepts a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" /> directly. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="target">The user to block — a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" />.</param>
<returns>A <see cref="BlockRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>BlockRef</c> to <see cref="unblock" /> to undo.</returns>
<summary> Block a user by handle string. The handle is resolved to a DID, then the block is created. Also accepts a DID string directly (if it starts with <c>did:</c>, it is parsed as a DID). </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="identifier">A handle (e.g., <c>my-handle.bsky.social</c>) or DID string (e.g., <c>did:plc:abc123</c>).</param>
<returns>A <see cref="BlockRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>BlockRef</c> to <see cref="unblock" /> to undo.</returns>
<remarks> For type-safe usage when you already have a <see cref="Did" />, use <see cref="block" /> instead. </remarks>
<summary> Block an entire moderation list. Creates a <c>app.bsky.graph.listblock</c> record. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="listUri">The AT-URI of the moderation list to block.</param>
<returns>A <see cref="ListBlockRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>ListBlockRef</c> to <see cref="unblockModList" /> to undo.</returns>
<summary> Unblock a previously blocked moderation list by deleting the list block record. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="listBlockRef">The <see cref="ListBlockRef" /> returned by <see cref="blockModList" />.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Mute an account. Accepts a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" /> directly. Muted accounts are hidden from your feeds but not blocked. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="target">The user to mute — a <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" />.</param>
<returns><c>unit</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Mute an account by handle string. The handle is resolved to a DID, then the mute is created. Also accepts a DID string directly (if it starts with <c>did:</c>, it is parsed as a DID). </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="identifier">A handle (e.g., <c>my-handle.bsky.social</c>) or DID string (e.g., <c>did:plc:abc123</c>).</param>
<returns><c>unit</c> on success, or an <see cref="XrpcError" />.</returns>
<remarks> For type-safe usage when you already have a <see cref="Did" />, use <see cref="muteUser" /> instead. </remarks>
<summary> Mute a thread. Posts in the muted thread are hidden from your notifications. Accepts an <see cref="AtUri" />, <see cref="PostRef" />, or <see cref="TimelinePost" />. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="root">The thread root post (an <see cref="AtUri" />, <see cref="PostRef" />, or <see cref="TimelinePost" />).</param>
<returns><c>unit</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Mute an entire moderation list. All accounts on the list are muted. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="listUri">The AT-URI of the moderation list to mute.</param>
<returns><c>unit</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Like a post or other record. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="target">A <see cref="PostRef" /> or <see cref="TimelinePost" /> identifying the record to like.</param>
<returns>A <see cref="LikeRef" /> on success, or an <see cref="XrpcError" />. Pass the <c>LikeRef</c> to <see cref="unlike" /> to undo.</returns>
<summary> Generic undo: delete any ref type (<see cref="LikeRef" />, <see cref="RepostRef" />, <see cref="FollowRef" />, or <see cref="BlockRef" />). Dispatches to the correct typed undo function via SRTP. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="ref">Any ref type with an AT-URI (LikeRef, RepostRef, FollowRef, or BlockRef).</param>
<returns><c>Ok Undone</c> on success, or an <see cref="XrpcError" />. Note: the AT Protocol's deleteRecord is idempotent, so this always returns <c>Undone</c> even if the record was already deleted. Only target-based functions (<see cref="unlikePost" />/<see cref="unrepostPost" />) can return <c>WasNotPresent</c>. </returns>
<example><code> let! likeRef = Bluesky.like agent postRef let! result = Bluesky.undo agent likeRef // works with any ref type </code></example>
<summary> Get a user's profile. Accepts a <see cref="Handle" />, <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" />. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="actor">A <see cref="Handle" />, <see cref="Did" />, <see cref="ProfileSummary" />, or <see cref="Profile" />.</param>
<returns>A <see cref="Profile" /> on success, or an <see cref="XrpcError" />.</returns>
<summary> Get the authenticated user's home timeline. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="limit">Maximum number of posts to return (optional, pass <c>None</c> for server default).</param>
<param name="cursor">Pagination cursor from a previous response (optional, pass <c>None</c> to start from the beginning).</param>
<returns>A page of <see cref="FeedItem" /> with an optional cursor, or an <see cref="XrpcError" />.</returns>