ProtoRune.Bsky (proto_rune v0.5.2)

Copy Markdown

High-level Bluesky API helpers.

Provides ergonomic wrappers around repository operations and XRPC calls for common Bluesky tasks.

Examples

# Post
{:ok, post} = Bsky.post(session, "Hello!")

# Like
{:ok, like} = Bsky.like(session, post_uri, post_cid)

# Follow
{:ok, follow} = Bsky.follow(session, "alice.bsky.social")

# Get profile
{:ok, profile} = Bsky.get_profile(session, "bob.bsky.social")

Summary

Functions

Blocks an actor.

Deletes a post by its AT-URI.

Follows an actor.

Gets a post thread with context.

Gets multiple posts by their AT-URIs.

Gets an actor's profile.

Gets multiple actor profiles.

Gets the authenticated user's timeline.

Gets the count of unread notifications.

Likes a post.

Lists notifications for the authenticated user.

Mutes an actor (client-side muting via XRPC).

Posts a text message to Bluesky.

Reposts a post.

Searches for actors (profiles) matching a query.

Searches for posts matching a query.

Unblocks an actor by deleting the block record.

Unfollows an actor by deleting the follow record.

Unlikes a post by deleting the like record.

Unmutes an actor.

Unrepost by deleting the repost record.

Updates the authenticated user's profile.

Marks notifications as seen up to a given timestamp.

Types

session()

@type session() :: ProtoRune.Session.t()

Functions

block(session, actor)

@spec block(session(), String.t()) :: {:ok, map()} | {:error, term()}

Blocks an actor.

Examples

{:ok, block} = Bsky.block(session, "spammer.bsky.social")
{:ok, block} = Bsky.block(session, "did:plc:xyz123")

delete_post(session, post_uri)

@spec delete_post(session(), String.t()) :: :ok | {:error, term()}

Deletes a post by its AT-URI.

Examples

:ok = Bsky.delete_post(session, post.uri)

follow(session, actor)

@spec follow(session(), String.t()) :: {:ok, map()} | {:error, term()}

Follows an actor.

Examples

{:ok, follow} = Bsky.follow(session, "alice.bsky.social")
{:ok, follow} = Bsky.follow(session, "did:plc:abc123")

get_post_thread(session, uri, opts \\ [])

@spec get_post_thread(session(), String.t(), keyword()) ::
  {:ok, map()} | {:error, term()}

Gets a post thread with context.

Options

  • :depth - How many levels of replies to fetch (default: 6)
  • :parent_height - How many levels of parent posts to fetch (default: 80)

Examples

{:ok, thread} = Bsky.get_post_thread(session, post_uri)

get_posts(session, uris)

@spec get_posts(session(), [String.t()]) :: {:ok, map()} | {:error, term()}

Gets multiple posts by their AT-URIs.

Examples

uris = ["at://did:plc:xyz/app.bsky.feed.post/123", "at://..."]
{:ok, posts} = Bsky.get_posts(session, uris)

get_profile(session, actor)

@spec get_profile(session(), String.t()) :: {:ok, map()} | {:error, term()}

Gets an actor's profile.

Examples

{:ok, profile} = Bsky.get_profile(session, "alice.bsky.social")

get_profiles(session, actors)

@spec get_profiles(session(), [String.t()]) :: {:ok, map()} | {:error, term()}

Gets multiple actor profiles.

Examples

{:ok, profiles} = Bsky.get_profiles(session, ["alice.bsky.social", "bob.bsky.social"])

get_timeline(session, opts \\ [])

@spec get_timeline(
  session(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Gets the authenticated user's timeline.

Options

  • :limit - Number of posts (default: 50, max: 100)
  • :cursor - Pagination cursor

Examples

{:ok, %{feed: posts, cursor: cursor}} = Bsky.get_timeline(session)
{:ok, %{feed: more}} = Bsky.get_timeline(session, cursor: cursor)

get_unread_count(session)

@spec get_unread_count(session()) :: {:ok, map()} | {:error, term()}

Gets the count of unread notifications.

Examples

{:ok, %{count: unread}} = Bsky.get_unread_count(session)

like(session, uri, cid)

@spec like(session(), String.t(), String.t()) :: {:ok, map()} | {:error, term()}

Likes a post.

Examples

{:ok, like} = Bsky.like(session, post.uri, post.cid)

list_notifications(session, opts \\ [])

@spec list_notifications(
  session(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Lists notifications for the authenticated user.

Options

  • :limit - Number of notifications (default: 50)
  • :cursor - Pagination cursor
  • :seen_at - Only return notifications after this timestamp

Examples

{:ok, %{notifications: notifs, cursor: cursor}} = Bsky.list_notifications(session)

mute(session, actor)

@spec mute(session(), String.t()) :: {:ok, map()} | {:error, term()}

Mutes an actor (client-side muting via XRPC).

Examples

{:ok, _} = Bsky.mute(session, "noisy.bsky.social")

post(session, text, opts \\ [])

@spec post(session(), String.t() | map(), keyword()) ::
  {:ok, map()} | {:error, term()}

Posts a text message to Bluesky.

Supports both plain text strings and RichText structs with facets.

Options

  • :langs - List of language codes (default: ["en"])
  • :reply_to - AT-URI of post to reply to
  • :created_at - Timestamp (default: now)

Examples

# Simple text post
{:ok, post} = Bsky.post(session, "Hello Bluesky!")

# Reply to a post
{:ok, reply} = Bsky.post(session, "Great point!",
  reply_to: "at://did:plc:xyz/app.bsky.feed.post/3k..."
)

# Rich text with mentions and links
alias ProtoRune.RichText

{:ok, rt} =
  RichText.new()
  |> RichText.text("Hello ")
  |> RichText.mention("alice.bsky.social")
  |> RichText.text("!")
  |> RichText.build()

{:ok, post} = Bsky.post(session, rt)

repost(session, uri, cid)

@spec repost(session(), String.t(), String.t()) :: {:ok, map()} | {:error, term()}

Reposts a post.

Examples

{:ok, repost} = Bsky.repost(session, post.uri, post.cid)

search_actors(session, query, opts \\ [])

@spec search_actors(session(), String.t(), keyword()) ::
  {:ok, map()} | {:error, term()}

Searches for actors (profiles) matching a query.

Options

  • :limit - Number of actors (default: 25, max: 100)
  • :cursor - Pagination cursor

Examples

{:ok, %{actors: actors}} = Bsky.search_actors(session, "alice")

search_posts(session, query, opts \\ [])

@spec search_posts(session(), String.t(), keyword()) ::
  {:ok, map()} | {:error, term()}

Searches for posts matching a query.

Options

  • :sort - :top or :latest
  • :since - Only posts after this Date
  • :until - Only posts before this Date
  • :author - Restrict to posts by this actor (handle or DID)
  • :lang - Restrict to this language code
  • :domain - Restrict to posts linking to this domain
  • :url - Restrict to posts linking to this URL
  • :mentions - Restrict to posts mentioning these actors
  • :tag - Restrict to posts with these hashtags
  • :limit - Number of posts (default: 25, max: 100)
  • :cursor - Pagination cursor

Examples

{:ok, %{posts: posts}} = Bsky.search_posts(session, "elixir lang")
{:ok, %{posts: latest}} = Bsky.search_posts(session, "elixir", sort: :latest)

unblock(session, block_uri)

@spec unblock(session(), String.t()) :: :ok | {:error, term()}

Unblocks an actor by deleting the block record.

Examples

:ok = Bsky.unblock(session, block.uri)

unfollow(session, follow_uri)

@spec unfollow(session(), String.t()) :: :ok | {:error, term()}

Unfollows an actor by deleting the follow record.

Examples

:ok = Bsky.unfollow(session, follow.uri)

unlike(session, like_uri)

@spec unlike(session(), String.t()) :: :ok | {:error, term()}

Unlikes a post by deleting the like record.

Examples

:ok = Bsky.unlike(session, like.uri)

unmute(session, actor)

@spec unmute(session(), String.t()) :: {:ok, map()} | {:error, term()}

Unmutes an actor.

Examples

{:ok, _} = Bsky.unmute(session, "noisy.bsky.social")

unrepost(session, repost_uri)

@spec unrepost(session(), String.t()) :: :ok | {:error, term()}

Unrepost by deleting the repost record.

Examples

:ok = Bsky.unrepost(session, repost.uri)

update_profile(session, updates)

@spec update_profile(
  session(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Updates the authenticated user's profile.

Fetches the current app.bsky.actor.profile record, merges the given changes, and writes it back, so fields not mentioned are preserved.

Options

  • :display_name - New display name
  • :description - New profile description (bio)
  • :avatar - {data, content_type} tuple with the raw image bytes and its MIME type. The data is uploaded as a blob and linked in the record.

Examples

{:ok, _} = Bsky.update_profile(session, display_name: "Alice")

{:ok, _} =
  Bsky.update_profile(session,
    display_name: "Alice",
    description: "Posting about Elixir",
    avatar: {File.read!("avatar.png"), "image/png"}
  )

update_seen(session, seen_at)

@spec update_seen(session(), DateTime.t()) :: {:ok, map()} | {:error, term()}

Marks notifications as seen up to a given timestamp.

Examples

:ok = Bsky.update_seen(session, DateTime.utc_now())