Notifications
Fetch, count, and mark notifications as read through the Bluesky module.
All examples use taskResult {} -- see the Error Handling guide for details.
Domain Types
Notification
A notification from the user's notification feed.
Field |
Type |
Description |
|---|---|---|
|
|
AT-URI of the notification record |
|
|
The user who triggered the notification |
|
|
Rich discriminated union with type-specific data |
|
|
Whether the notification has been seen |
|
|
When the notification was indexed |
NotificationContent
Rich discriminated union for notification types. Uses [<RequireQualifiedAccess>], so all cases must be qualified (e.g. NotificationContent.Like). Each case carries the data relevant to that notification kind.
Case |
Fields |
Description |
|---|---|---|
|
|
Someone liked your post |
|
|
Someone reposted your post |
|
-- |
Someone followed you |
|
|
Someone replied to your post |
|
|
Someone mentioned you in a post |
|
|
Someone quoted your post |
|
|
Someone joined via your starter pack |
|
|
A notification type not yet recognized by the library |
Functions
Reading
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Fetch a page of notifications |
|
|
|
Get the number of unseen notifications |
taskResult {
let! count = Bluesky.getUnreadNotificationCount agent
printfn "You have %d unread notifications" count
()
}
taskResult {
let! page = Bluesky.getNotifications agent (Some 25L) None
for n in page.Items do
match n.Content with
| NotificationContent.Like postRef ->
printfn "%s liked your post (%O)" n.Author.DisplayName postRef.Uri
| NotificationContent.Follow ->
printfn "%s followed you" n.Author.DisplayName
| NotificationContent.Reply (text, _) ->
printfn "%s replied: %s" n.Author.DisplayName text
| NotificationContent.Mention text ->
printfn "%s mentioned you: %s" n.Author.DisplayName text
| NotificationContent.Repost postRef ->
printfn "%s reposted (%O)" n.Author.DisplayName postRef.Uri
| NotificationContent.Quote (text, _) ->
printfn "%s quoted you: %s" n.Author.DisplayName text
| _ -> printfn "Other notification from %s" n.Author.DisplayName
}
Actions
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Mark all notifications as seen up to the current time |
The protocol treats "seen" as a high-water mark -- there is no way to mark individual notifications. All notifications up to the current timestamp are marked as seen.
taskResult {
do! Bluesky.markNotificationsSeen agent
}
Pagination
Function |
Accepts |
Returns |
Description |
|---|---|---|---|
|
|
|
Lazily paginate all notifications |
The paginator returns an IAsyncEnumerable that fetches pages on demand and stops when the server has no more results:
task {
let pages = Bluesky.paginateNotifications agent (Some 50L)
let enumerator = pages.GetAsyncEnumerator()
let rec loop () = task {
let! hasNext = enumerator.MoveNextAsync()
if hasNext then
match enumerator.Current with
| Ok page ->
for n in page.Items do
printfn "%s: %A" n.Author.DisplayName n.Content
| Error err ->
printfn "Error: %A" err
do! loop ()
}
do! loop ()
do! enumerator.DisposeAsync()
}
See the Pagination guide for more patterns on consuming IAsyncEnumerable from F#.
Complete Workflow
A typical notification check: read the unread count, fetch if there are any, process them, then mark as seen.
open FSharp.ATProto.Core
open FSharp.ATProto.Bluesky
taskResult {
let! agent = Bluesky.login "https://bsky.social" "handle.bsky.social" "app-password"
let! unread = Bluesky.getUnreadNotificationCount agent
if unread > 0L then
let! page = Bluesky.getNotifications agent (Some 50L) None
for n in page.Items do
if not n.IsRead then
match n.Content with
| NotificationContent.Follow ->
printfn "New follower: %s (%O)" n.Author.DisplayName n.Author.Handle
| NotificationContent.Like postRef
| NotificationContent.Repost postRef ->
printfn "%s interacted with %O" n.Author.DisplayName postRef.Uri
| NotificationContent.Reply (text, _) ->
printfn "%s replied: %s" n.Author.DisplayName text
| NotificationContent.Mention text ->
printfn "%s mentioned you: %s" n.Author.DisplayName text
| NotificationContent.Quote (text, _) ->
printfn "%s quoted you: %s" n.Author.DisplayName text
| _ -> ()
do! Bluesky.markNotificationsSeen agent
printfn "Marked %d notifications as seen" unread
else
printfn "No new notifications"
}
Power Users: Raw XRPC
For fields the Notification domain type does not expose (such as Labels or the raw Record JSON), drop to the generated XRPC wrapper:
taskResult {
let! output =
AppBskyNotification.ListNotifications.query agent
{ Cursor = None
Limit = Some 10L
Priority = None
Reasons = None
SeenAt = None }
for n in output.Notifications do
printfn "%O (%A) - labels: %A" n.Uri n.Reason n.Labels
}
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>
<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> Get the count of unread notifications for the authenticated user. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<returns>The unread notification count on success, or an <see cref="XrpcError" />.</returns>
<summary> List notifications for the authenticated user. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="limit">Maximum number of notifications 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="Notification" /> with an optional cursor, or an <see cref="XrpcError" />.</returns>
<summary>The content of a notification, varying by kind.</summary>
<summary>The AT-URI of the post record.</summary>
<summary> Mark all notifications as seen up to the current time. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<returns><c>unit</c> on success, or an <see cref="XrpcError" />.</returns>
<summary> Paginate notifications for the authenticated user. Returns an async enumerable of pages. Each element is a <c>Result</c> containing one page of notifications. Pagination stops automatically when the server returns no cursor. </summary>
<param name="agent">An authenticated <see cref="AtpAgent" />.</param>
<param name="pageSize">Maximum number of notifications per page (optional, pass <c>None</c> for server default).</param>
<returns>An <see cref="System.Collections.Generic.IAsyncEnumerable{T}" /> of paginated results.</returns>
<summary>Gets the element in the collection at the current position of the enumerator.</summary>
<returns>The element in the collection at the current position of the enumerator.</returns>