Header menu logo FSharp.ATProto

Preferences

FSharp.ATProto provides convenience functions for reading and modifying Bluesky user preferences. Each function performs a read-modify-write under the hood via upsertPreferences, so concurrent modifications are safe.

All examples use taskResult {}. See the Error Handling guide for details.

open FSharp.ATProto.Core
open FSharp.ATProto.Bluesky
open FSharp.ATProto.Syntax

Functions

Function

Description

Bluesky.getPreferences

Read current preferences as raw JSON elements

Bluesky.upsertPreferences

Read-modify-write preferences atomically

Bluesky.addSavedFeed

Add a feed to saved feeds

Bluesky.removeSavedFeed

Remove a feed from saved feeds by ID

Bluesky.addMutedWord

Add a muted word

Bluesky.removeMutedWord

Remove a muted word by value

Bluesky.setContentLabelPref

Set visibility for a content label

Bluesky.setAdultContentEnabled

Enable or disable adult content

Bluesky.setThreadViewPref

Set thread sorting preference

Bluesky.addHiddenPost

Hide a post from your feeds

Bluesky.removeHiddenPost

Unhide a post

Muted Words

Muted words hide posts containing the word from your feeds and notifications. The addMutedWord function takes an AppBskyActor.Defs.MutedWord record:

taskResult {
    let! agent = Bluesky.login "https://bsky.social" "handle.bsky.social" "app-password"

    // Mute a word in post content and tags
    do! Bluesky.addMutedWord agent
            { Value = "spoilers"
              Targets = [ AppBskyActor.Defs.MutedWordTarget.Content
                          AppBskyActor.Defs.MutedWordTarget.Tag ]
              ActorTarget = Some AppBskyActor.Defs.MutedWordActorTarget.All
              ExpiresAt = None
              Id = None }

    // Remove it later
    do! Bluesky.removeMutedWord agent "spoilers"
}

Saved Feeds

Add or remove feeds from the user's saved feeds list:

taskResult {
    let feed : AppBskyActor.Defs.SavedFeed =
        { Id = System.Guid.NewGuid().ToString()
          Type = AppBskyActor.Defs.SavedFeedType.Feed
          Value = AtUri.value feedGeneratorUri
          Pinned = true }

    do! Bluesky.addSavedFeed agent feed

    // Remove by the feed ID
    do! Bluesky.removeSavedFeed agent feed.Id
}

Content Filtering

Control how content labels affect your experience. The ContentLabelPrefVisibility DU has these cases:

Case

Effect

Show

Always show labeled content

Warn

Show a warning overlay (default for most labels)

Hide

Hide labeled content entirely

Ignore

Ignore the label

taskResult {
    // Warn on NSFW content instead of hiding
    do! Bluesky.setContentLabelPref agent
            "nsfw"
            AppBskyActor.Defs.ContentLabelPrefVisibility.Warn
            None // None = Bluesky's built-in labeler

    // Set preference for a custom labeler
    do! Bluesky.setContentLabelPref agent
            "spoiler"
            AppBskyActor.Defs.ContentLabelPrefVisibility.Hide
            (Some labelerDid)
}

Adult Content

Toggle the adult content master switch:

do! Bluesky.setAdultContentEnabled agent true

Thread Sorting

Set how replies are sorted when viewing a thread. The ThreadViewPrefSort DU options:

Case

Description

Oldest

Chronological order (oldest first)

Newest

Reverse chronological (newest first)

MostLikes

Sort by like count

Random

Random order

Hotness

Sort by engagement

do! Bluesky.setThreadViewPref agent AppBskyActor.Defs.ThreadViewPrefSort.Oldest

Hidden Posts

Hide or unhide individual posts from your feeds:

taskResult {
    do! Bluesky.addHiddenPost agent postUri
    // later...
    do! Bluesky.removeHiddenPost agent postUri
}

Custom Preference Updates

For modifications not covered by the convenience functions, use upsertPreferences directly. It reads the current preferences, applies your update function, and writes the result back:

taskResult {
    do! Bluesky.upsertPreferences agent (fun prefs ->
        // prefs is a JsonElement list -- filter, modify, or append
        prefs |> List.filter (fun el ->
            // your custom logic here
            true))
}
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.ATProto
namespace FSharp.ATProto.Syntax
namespace FSharp.ATProto.Core
namespace FSharp.ATProto.Bluesky
val agent: AtpAgent
module Unchecked from Microsoft.FSharp.Core.Operators
val defaultof<'T> : 'T
Multiple items
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>
val feedGeneratorUri: AtUri
Multiple items
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://&lt;authority&gt;[/&lt;collection&gt;[/&lt;rkey&gt;]]</c>. Maximum length is 8192 characters. </summary>
<remarks> See the AT Protocol specification: https://atproto.com/specs/at-uri-scheme </remarks>
val labelerDid: Did
Multiple items
module Did from FSharp.ATProto.Syntax
<summary> Functions for creating, validating, and extracting data from <see cref="Did" /> values. </summary>

--------------------
type Did = private | Did of string override ToString: unit -> string
<summary> A decentralized identifier (DID) as defined by the AT Protocol. DIDs are the primary stable identifier for accounts. Two methods are currently supported: <c>did:plc:</c> (hosted, managed by PLC directory) and <c>did:web:</c> (self-hosted, DNS-based). </summary>
<remarks> See the AT Protocol specification: https://atproto.com/specs/did and the W3C DID specification: https://www.w3.org/TR/did-core/ </remarks>
val postUri: AtUri
val taskResult: TaskResultBuilder
module Bluesky from FSharp.ATProto.Bluesky
<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>
val login: baseUrl: string -> identifier: string -> password: string -> System.Threading.Tasks.Task<Result<AtpAgent,XrpcError>>
val addMutedWord: agent: AtpAgent -> word: AppBskyActor.Defs.MutedWord -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Add a muted word to the user's preferences. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="word">The <see cref="AppBskyActor.Defs.MutedWord" /> to add.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
module AppBskyActor from FSharp.ATProto.Bluesky
module Defs from FSharp.ATProto.Bluesky.AppBskyActor
type MutedWordTarget = | Content | Tag | Unknown of string
union case AppBskyActor.Defs.MutedWordTarget.Content: AppBskyActor.Defs.MutedWordTarget
union case AppBskyActor.Defs.MutedWordTarget.Tag: AppBskyActor.Defs.MutedWordTarget
union case Option.Some: Value: 'T -> Option<'T>
type MutedWordActorTarget = | All | ExcludeFollowing | Unknown of string
union case AppBskyActor.Defs.MutedWordActorTarget.All: AppBskyActor.Defs.MutedWordActorTarget
union case Option.None: Option<'T>
val removeMutedWord: agent: AtpAgent -> wordValue: string -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Remove a muted word from the user's preferences by value. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="wordValue">The muted word string to remove.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val feed: AppBskyActor.Defs.SavedFeed
type SavedFeed = { Id: string Pinned: bool Type: SavedFeedType Value: string }
namespace System
Multiple items
[<Struct>] type Guid = new: b: byte array -> unit + 6 overloads member CompareTo: value: Guid -> int + 1 overload member Equals: g: Guid -> bool + 1 overload member GetHashCode: unit -> int member ToByteArray: unit -> byte array + 1 overload member ToString: unit -> string + 2 overloads member TryFormat: utf8Destination: Span<byte> * bytesWritten: byref<int> * ?format: ReadOnlySpan<char> -> bool + 1 overload member TryWriteBytes: destination: Span<byte> -> bool + 1 overload static member (<) : left: Guid * right: Guid -> bool static member (<=) : left: Guid * right: Guid -> bool ...
<summary>Represents a globally unique identifier (GUID).</summary>

--------------------
System.Guid ()
System.Guid(b: byte array) : System.Guid
System.Guid(b: System.ReadOnlySpan<byte>) : System.Guid
System.Guid(g: string) : System.Guid
System.Guid(b: System.ReadOnlySpan<byte>, bigEndian: bool) : System.Guid
System.Guid(a: int, b: int16, c: int16, d: byte array) : System.Guid
System.Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : System.Guid
System.Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : System.Guid
System.Guid.NewGuid() : System.Guid
type SavedFeedType = | Feed | List | Timeline | Unknown of string
union case AppBskyActor.Defs.SavedFeedType.Feed: AppBskyActor.Defs.SavedFeedType
val value: AtUri -> string
<summary> Extract the string representation of an AT-URI. </summary>
<param name="atUri">The AT-URI to extract the value from.</param>
<returns>The full AT-URI string (e.g. <c>"at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3k2la3b"</c>).</returns>
val addSavedFeed: agent: AtpAgent -> feed: AppBskyActor.Defs.SavedFeed -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Add a saved feed to the user's preferences. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="feed">The <see cref="AppBskyActor.Defs.SavedFeed" /> to add.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val removeSavedFeed: agent: AtpAgent -> feedId: string -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Remove a saved feed from the user's preferences by its ID. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="feedId">The ID of the saved feed to remove.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
AppBskyActor.Defs.SavedFeed.Id: string
val setContentLabelPref: agent: AtpAgent -> label: string -> visibility: AppBskyActor.Defs.ContentLabelPrefVisibility -> labelerDid: Did option -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Set the visibility for a content label. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="label">The label identifier (e.g. "nsfw", "gore").</param>
<param name="visibility">The desired visibility setting.</param>
<param name="labelerDid">Optional labeler DID (for custom labelers).</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
type ContentLabelPrefVisibility = | Ignore | Show | Warn | Hide | Unknown of string
union case AppBskyActor.Defs.ContentLabelPrefVisibility.Warn: AppBskyActor.Defs.ContentLabelPrefVisibility
union case AppBskyActor.Defs.ContentLabelPrefVisibility.Hide: AppBskyActor.Defs.ContentLabelPrefVisibility
val setAdultContentEnabled: agent: AtpAgent -> enabled: bool -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Enable or disable adult content in the user's preferences. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="enabled">Whether adult content should be enabled.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val setThreadViewPref: agent: AtpAgent -> sort: AppBskyActor.Defs.ThreadViewPrefSort -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Set thread view preferences (sort order). </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="sort">The desired thread sort order.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
type ThreadViewPrefSort = | Oldest | Newest | MostLikes | Random | Hotness | Unknown of string
union case AppBskyActor.Defs.ThreadViewPrefSort.Oldest: AppBskyActor.Defs.ThreadViewPrefSort
val addHiddenPost: agent: AtpAgent -> postUri: AtUri -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Add a post URI to the hidden posts list. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="postUri">The AT-URI of the post to hide.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val removeHiddenPost: agent: AtpAgent -> postUri: AtUri -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Remove a post URI from the hidden posts list. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="postUri">The AT-URI of the post to unhide.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val upsertPreferences: agent: AtpAgent -> updateFn: (System.Text.Json.JsonElement list -> System.Text.Json.JsonElement list) -> System.Threading.Tasks.Task<Result<unit,XrpcError>>
<summary> Read-modify-write the authenticated user's preferences. Reads current preferences, applies your transform function, and writes the result back. Preferences use last-write-wins semantics (no CAS). </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="updateFn">A function that receives the current preferences list and returns the updated list.</param>
<returns><c>Ok ()</c> on success, or an <see cref="XrpcError" />.</returns>
val prefs: System.Text.Json.JsonElement list
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T with get member IsEmpty: bool with get member Item: index: int -> 'T with get ...
val filter: predicate: ('T -> bool) -> list: 'T list -> 'T list
val el: System.Text.Json.JsonElement

Type something to start searching.