NAME

Bluesky - Bluesky Client Library in Perl

SYNOPSIS

use Bluesky;
my $bsky = Bluesky->new( );

# Recommended: log in once and reuse the saved session on later runs
$bsky->ensure_auth( handle => 'user.bsky.social' );

# Or drive the interactive OAuth flow directly:
$bsky->oauth_helper(
    handle     => 'user.bsky.social',
    listen     => 1, # Automatically catch the redirect
    on_success => sub { say 'Logged in!' }
);

# Or use an app password (not recommended)
$bsky->login( 'sanko', '1111-2222-3333-4444' );

# Posting
$bsky->createPost( text => 'Hello from Perl!' );

# Streaming
my $fh = $bsky->firehose(sub ( $header, $body, $err ) {
    return warn $err if $err;
    say 'New event: ' . $header->{t};
});
$fh->start();

DESCRIPTION

You shouldn't need to know the AT protocol in order to get things done so I'm including this sugary wrapper so that At can remain mostly technical.

Constructor and Session Management

Bluesky.pm is my attempt to make use of Perl's class syntax so this is obviously OO.

new( [...] )

my $bsky = Bluesky->new( );

Create a new Bluesky client object.

Expected parameters include:

ensure_auth( %args )

$bsky->ensure_auth( handle => 'you.bsky.social' );

Convenience wrapper for applications that want to stay logged in across runs. When file exists it loads and resumes the saved session (re-saving it if the underlying tokens were rotated); otherwise it runs an interactive OAuth flow via oauth_helper and saves the resulting session.

Expected parameters include:

oauth_helper( %args )

A high-level helper to manage the entire OAuth flow. This is the recommended way to authenticate for interactive applications.

$bsky->oauth_helper(
    handle     => 'user.bsky.social',
    listen     => 1,
    on_success => sub ($self) {
        say 'Authenticated as ' . $self->did;
    }
);

Expected parameters include:

oauth_start( $handle, $client_id, $redirect_uri, [ $scope ] )

Initiates the OAuth 2.0 Authorization Code flow. Returns the authorization URL.

$scope defaults to atproto if omitted.

my $url = $bsky->oauth_start(
    'user.bsky.social',
    'http://localhost',
    'http://127.0.0.1:8888/callback'
);

The $scope here defaults to simply 'atproto'. See https://atproto.com/guides/scope-builder to build your own scope strings.

oauth_callback( $code, $state )

Exchanges the authorization code for tokens and completes the OAuth flow.

$bsky->oauth_callback( $code, $state );

login( $identifier, $password )

$bsky->login( 'sanko', '1111-2222-3333-4444' );

Expected parameters include:

resume( $accessJwt, $refreshJwt, [ $token_type, ... ] )

$bsky->resume( $accessJwt, $refreshJwt );

Restores a session from existing tokens. Delegates to "resume" in At.

session( )

my $session = $bsky->session();

Returns the underlying At session data.

did( )

my $did = $bsky->did();

Returns the DID of the authenticated user, if any.

save_session( [ $file ] )

$bsky->save_session();    # writes session.json

Serializes the current session to $file (default session.json) so it can be restored later with load_session or ensure_auth. Does nothing, with a warning, when there is no active session.

load_session( [ $file ] )

$bsky->load_session();

Reads a previously saved session from $file (default session.json) and resumes it. Returns false when the file does not exist or cannot be parsed.

firehose( $callback, [ $url ] )

Returns a new At::Protocol::Firehose client for real-time streaming.

my $fh = $bsky->firehose(sub ($header, $body, $err) { ... });
$fh->start();

See At::Protocol::Firehose for more details.

Feed and Content

Methods in this category create, modify, access, and delete content.

getTrendingTopics( [...] )

$bsky->getTrendingTopics( );

Get a list of trending topics.

Expected parameters include:

getTimeline( [...] )

$bsky->getTimeline();

Get a view of the requesting account's home timeline. This is expected to some form of reverse-chronological feed.

Expected parameters include:

getFeed( $feed, [...] )

$bsky->getFeed( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/3l6oveex3ii2l' );

Get a hydrated feed from a feed generator. $feed is the feed generator AT-URI. Returns the feed arrayref.

getFeedSkeleton( $feed, [...] )

$bsky->getFeedSkeleton( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/3l6oveex3ii2l' );

Get a feed skeleton (list of URIs) from a feed generator. $feed is the feed generator AT-URI. Returns the feed arrayref.

getListFeed( $list, [...] )

$bsky->getListFeed( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3l6oveex3ii2l' );

Get a feed of recent posts from a list (posts and reposts from any actors on the list). Does not require auth. Returns the feed arrayref.

sendInteractions( [...] )

$bsky->sendInteractions( interactions => [ { item => $uri, event => 'app.bsky.feed.defs#interactionSeen' } ] );

Send information about interactions with feed items back to the feed generator that served them. Accepts interactions (required) and an optional feed AT-URI.

getAuthorFeed( ... )

$bsky->getAuthorFeed( actor => 'sankorobinson.com' );

Get a view of an actor's 'author feed' (post and reposts by the author).

Expected parameters include:

An error is returned if the client is blocked by the actor.

getPostThread( ... )

$bsky->getPostThread( uri => 'at://bsky.app/app.bsky.feed.post/3l6oveex3ii2l' );

Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests.

Expected parameters include:

Returns an error if the thread cannot be found.

getPost( ... )

$bsky->getPost('at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2');

Gets a single post view for a specified post (by AT-URI).

Expected parameters include:

getPosts( ... )

$bsky->getPosts(
    'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l',
    'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3lbvgvbvcf22c'
);

Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'.

Expected parameters include:

getLikes( ... )

$bsky->getLikes( uri => 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Get like records which reference a subject (by AT-URI and CID).

Expected parameters include:

getBookmarks( ... )

$bsky->getBookmarks();

Get private bookmarks for the authorized account.

createBookmark( ... )

$bsky->createBookmark( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Create a private bookmark for a post.

deleteBookmark( ... )

$bsky->deleteBookmark( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Delete a private bookmark.

getQuotes( ... )

$bsky->getQuotes( uri => 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Get quotes of a post.

getActorLikes( ... )

$bsky->getActorLikes( actor => 'sankorobinson.com' );

Get a list of posts liked by an actor.

searchPosts( ... )

$bsky->searchPosts( q => 'perl' );

Find posts matching search criteria.

getSuggestedFeeds( ... )

$bsky->getSuggestedFeeds();

Get suggested feed generators.

describeFeedGenerator( )

$bsky->describeFeedGenerator();

Get information about a feed generator.

getFeedGenerator( ... )

$bsky->getFeedGenerator( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/3l6oveex3ii2l' );

Get information about a feed generator.

getFeedGenerators( ... )

$bsky->getFeedGenerators( feeds => [ ... ] );

Get information about multiple feed generators.

getActorFeeds( ... )

$bsky->getActorFeeds( actor => 'sankorobinson.com' );

Get a list of feed generators created by an actor.

getRepostedBy( ... )

$bsky->getRepostedBy( uri => 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Get repost records which reference a subject (by AT-URI and CID).

createPost( ... )

$bsky->createPost( text => 'Test. Test. Test.' );

Create a new post.

Expected parameters include:

Note that a post may only contain one of the following embeds: images, gallery, video, embed_url, or embed_ref.

deletePost( ... )

$bsky->deletePost( 'at://did:plc:pwqewimhd3rxc4hg6ztwrcyj/app.bsky.feed.post/3lcdwvquo7y25' );

my $post = $bsky->createPost( ... );
...
$bsky->deletePost( $post->{uri} );

Delete a post or ensures it doesn't exist.

Expected parameters include:

like( ... )

$bsky->like( 'at://did:plc:pwqewimhd3rxc4hg6ztwrcyj/app.bsky.feed.post/3lcdwvquo7y25' );

$bsky->like( 'at://did:plc:totallymadeupgarbagehere/app.bsky.feed.post/randomexample', 'fu82qrfrf829crw89rfpuwcfiosdfcu8239wcrusiofcv2epcuy8r9jkfsl' );

Like a post publically.

Expected parameters include:

On success, a record is returned.

deleteLike( ... )

$bsky->deleteLike( 'at://did:plc:pwqewimhd3rxc4hg6ztwrcyj/app.bsky.feed.post/3lcdwvquo7y25' );

$bsky->deleteLike( 'at://did:plc:totallymadeupgarbagehere/app.bsky.feed.like/randomexample' );

Remove a like record.

Expected parameters include:

On success, commit info is returned.

repost( ... )

$bsky->repost( 'at://did:plc:pwqewimhd3rxc4hg6ztwrcyj/app.bsky.feed.post/3lcdwvquo7y25' );

Repost a post.

Expected parameters include:

deleteRepost( ... )

$bsky->deleteRepost( 'at://did:plc:pwqewimhd3rxc4hg6ztwrcyj/app.bsky.feed.repost/3lcdwvquo7y25' );

Remove a repost record.

uploadBlob( $data, [...] )

$bsky->uploadBlob( $data, mime_type => 'image/png' );

Upload a blob (file/data) to the PDS. See also "uploadFile( $bytes_or_path, [ $mime_type ] )"; it delegates to $bsky->at->upload_blob.

Social Graph

Methods documented in this section deal with relationships between the authorized user and other members of the social network.

block( $actor )

$bsky->block( 'sankorobinson.com' );

Blocks a user. $actor is the handle or DID of the person you'd like to block.

getBlocks( [...] )

$bsky->getBlocks( );

Enumerates which accounts the requesting account is currently blocking.

Requires auth.

Expected parameters include:

Returns a list of actor profile views on success.

deleteBlock( ... )

$bsky->deleteBlock( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.block/3l6oveex3ii2l' );

Unblocks a user by removing the block record.

follow( ... )

$bsky->follow( 'sankorobinson.com' );

Follows a user.

deleteFollow( ... )

$bsky->deleteFollow( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.follow/3l6oveex3ii2l' );

Unfollows a user by removing the follow record.

getFollows( ... )

$bsky->getFollows( 'sankorobinson.com' );

Enumerates who an account is following.

getFollowers( ... )

$bsky->getFollowers( 'sankorobinson.com' );

Enumerates who is following an account.

getKnownFollowers( ... )

$bsky->getKnownFollowers( 'sankorobinson.com' );

Enumerates followers of an account that the authorized user also follows (mutuals).

getRelationships( [...] )

$bsky->getRelationships( actors => ['sankorobinson.com', 'bsky.app'] );

Enumerates relationships between the authorized user and other actors. actors is accepted as an alias for others; actor defaults to the authenticated DID if omitted.

getMutes( ... )

$bsky->getMutes();

Enumerate actors that the authorized user has muted.

getListBlocks( [...] )

$bsky->getListBlocks();

Get mod lists that the requesting account is blocking. Requires auth. Returns the lists arrayref.

getListMutes( [...] )

$bsky->getListMutes();

Enumerate mod lists that the requesting account currently has muted. Requires auth. Returns the lists arrayref.

muteThread( ... )

$bsky->muteThread( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Mute a thread.

unmuteThread( ... )

$bsky->unmuteThread( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l' );

Unmute a thread.

getLists( ... )

$bsky->getLists( 'sankorobinson.com' );

Enumerate moderation lists created by an actor.

getList( ... )

$bsky->getList( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3l6oveex3ii2l' );

Get detailed view of a moderation list.

getListsWithMembership( $actor, [...] )

$bsky->getListsWithMembership( 'sankorobinson.com' );

Enumerate the lists created by the session user, including membership information about $actor in those lists. Requires auth. Returns the listsWithMembership arrayref.

getStarterPack( ... )

$bsky->getStarterPack( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.starterpack/3l6oveex3ii2l' );

Get a detailed view of a starter pack.

getStarterPacks( @uris )

$bsky->getStarterPacks(
    'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.starterpack/3l6oveex3ii2l',
    'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.starterpack/3l6oveex3ii2m'
);

Get views for a list of starter packs.

getActorStarterPacks( ... )

$bsky->getActorStarterPacks( 'sankorobinson.com' );

Get starter packs created by an actor.

getStarterPacksWithMembership( $actor, [...] )

$bsky->getStarterPacksWithMembership( 'sankorobinson.com' );

Enumerate the starter packs created by the session user, including membership information about $actor in those starter packs. Requires auth.

getSuggestedFollowsByActor( $actor )

$bsky->getSuggestedFollowsByActor( 'sankorobinson.com' );

Enumerate follows similar to a given account. Expected use is recommending additional accounts immediately after following one account. Returns the suggestions arrayref.

searchStarterPacks( ... )

$bsky->searchStarterPacks( q => 'perl' );

Find starter packs matching search criteria. Requires auth. Returns the starterPacks arrayref.

Actors

Methods in this section deal with profile information and actor discovery.

getProfile( ... )

$bsky->getProfile( 'sankorobinson.com' );

Get detailed profile view of an actor.

getPreferences( )

$bsky->getPreferences();

Get private preferences for the authorized account.

putPreferences( ... )

$bsky->putPreferences( [ ... ] );

Update private preferences for the authorized account.

upsertProfile( &callback )

$bsky->upsertProfile( sub (%existing) {
    return { %existing, displayName => 'New Name' };
});

Retrieve the current profile, allow a callback to modify it, and then update it.

getProfiles( ... )

$bsky->getProfiles( actors => ['sankorobinson.com', 'bsky.app'] );

Get detailed profile views of multiple actors.

getSuggestions( )

$bsky->getSuggestions();

Get a list of suggested actors.

searchActors( ... )

$bsky->searchActors( q => 'perl' );

Search for actors.

searchActorsTypeahead( ... )

$bsky->searchActorsTypeahead( q => 'san' );

Find actor suggestions for a partial search term.

mute( ... )

$bsky->mute( 'sankorobinson.com' );

Mutes an actor.

unmute( ... )

$bsky->unmute( 'sankorobinson.com' );

Unmutes an actor.

muteModList( ... )

$bsky->muteModList( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3l6oveex3ii2l' );

Mutes all actors in a moderation list.

unmuteModList( ... )

$bsky->unmuteModList( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3l6oveex3ii2l' );

Unmutes all actors in a moderation list.

blockModList( ... )

$bsky->blockModList( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3l6oveex3ii2l' );

Blocks all actors in a moderation list.

unblockModList( ... )

$bsky->unblockModList( 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.listblock/3l6oveex3ii2l' );

Unblocks a moderation list.

Moderation

Submits reports and manages moderation artifacts. For muting/blocking actors and lists, see "Actors".

report( $subject, $reason_type, [ $reason ] )

Submits a moderation report.

Expected parameters:

Notifications

Methods in this section deal with notifications.

listNotifications( ... )

$bsky->listNotifications();

Enumerate notifications for the authorized user.

countUnreadNotifications( )

$bsky->countUnreadNotifications();

Count unread notifications.

updateSeenNotifications( [ $seenAt ] )

$bsky->updateSeenNotifications();

Update when notifications were last seen.

getNotificationPreferences( )

$bsky->getNotificationPreferences();

Get notification-related preferences for the account. Requires auth.

putNotificationPreferences( ... )

$bsky->putNotificationPreferences( priority => 1 );

Set notification-related preferences for the account. Requires auth.

putNotificationPreferencesV2( ... )

$bsky->putNotificationPreferencesV2( like => { list => 1, push => 1, include => 'follows' } );

Set granular notification preferences (chat, like, quote, reply, follow, repost, mention, etc). Requires auth. Returns the updated preferences.

listActivitySubscriptions( [...] )

$bsky->listActivitySubscriptions();

Enumerate all accounts to which the requesting account is subscribed to receive notifications for. Requires auth. Returns the subscriptions arrayref.

putActivitySubscription( $subject, $activitySubscription )

$bsky->putActivitySubscription( 'did:plc:...', { post => 1, reply => 1 } );

Subscribe (or update a subscription) to activity notifications for $subject. Requires auth.

registerPush( ... )

$bsky->registerPush( serviceDid => 'did:web:...', token => '...', platform => 'ios', appId => '...' );

Register to receive push notifications via a specified service. Requires auth.

unregisterPush( ... )

$bsky->unregisterPush( serviceDid => 'did:web:...', token => '...', platform => 'ios', appId => '...' );

Stop push notifications for the given token. Requires auth.

Identity

Methods in this section deal with handle and DID resolution.

resolveHandle( ... )

$bsky->resolveHandle( 'sankorobinson.com' );

Resolves a handle to a DID.

updateHandle( ... )

$bsky->updateHandle( 'new-handle.bsky.social' );

Updates the handle for the authorized user.

describeServer( )

$bsky->describeServer();

Describes the server's account creation requirements and capabilities.

listRecords( ... )

$bsky->listRecords( repo => 'sankorobinson.com', collection => 'app.bsky.feed.post' );

List records in a repository collection.

getLabelerServices( ... )

$bsky->getLabelerServices( dids => [ ... ] );

Get views of labeler services.

queryLabels( ... )

$bsky->queryLabels( uriPatterns => [ 'at://did:plc:.../*' ] );

Find labels relevant to the provided AT-URI patterns. Accepts uriPatterns (required), sources, limit, and cursor. Returns the labels arrayref.

Chat

Methods in this section deal with direct messaging and conversations.

listConvos( [...] )

$bsky->listConvos();

Enumerates conversations for the authorized user.

getConvo( $convoId )

$bsky->getConvo( $convoId );

Get a detailed view of a conversation.

getConvoForMembers( members => [ ... ] )

$bsky->getConvoForMembers( members => [ 'did:plc:...' ] );

Get or create a conversation for a list of members.

getMessages( convoId => ..., [...] )

$bsky->getMessages( convoId => $convoId );

Get messages in a conversation.

sendMessage( $convoId, { text => ... } )

$bsky->sendMessage( $convoId, { text => 'Hello!' } );

Send a message to a conversation.

acceptConvo( $convoId )

$bsky->acceptConvo( $convoId );

Accept a conversation request.

leaveConvo( $convoId )

$bsky->leaveConvo( $convoId );

Leave a conversation.

updateRead( $convoId, [ $messageId ] )

$bsky->updateRead( $convoId );

Update the read status of a conversation.

muteConvo( $convoId )

$bsky->muteConvo( $convoId );

Mute a conversation.

unmuteConvo( $convoId )

$bsky->unmuteConvo( $convoId );

Unmute a conversation.

addReaction( $convoId, $messageId, $reaction )

$bsky->addReaction( $convoId, $messageId, '👍' );

Add a reaction to a message.

removeReaction( $convoId, $messageId, $reaction )

$bsky->removeReaction( $convoId, $messageId, '👍' );

Remove a reaction from a message.

deleteMessageForSelf( $convoId, $messageId )

$bsky->deleteMessageForSelf( $convoId, $messageId );

Delete a message for the local user.

getConvoAvailability( [...] )

$bsky->getConvoAvailability();

Check if the authorized user can join conversations.

getLog( [...] )

$bsky->getLog();

Get a log of chat events.

Video Services

Methods in this section deal with the video upload service.

getVideoUploadLimits( )

$bsky->getVideoUploadLimits();

Get video upload limits for the authenticated user (whether uploads are allowed, remaining daily bytes/videos).

getVideoJobStatus( $jobId )

$bsky->getVideoJobStatus( $jobId );

Get status details for a video processing job. Returns the jobStatus.

uploadVideoJob( $bytes_or_path, [ $mime_type, %opts ] )

$bsky->uploadVideoJob( 'path/to/clip.mp4', 'video/mp4', did => $bsky->did, name => 'clip.mp4' );

Upload a video to the video service (app.bsky.video.uploadVideo) for processing. Returns the jobStatus; poll "getVideoJobStatus( $jobId )" until blob is present, then use that blob in a post.

Optional %opts:

The video service cannot be reached through the PDS: uploadVideo posts the bytes to https://video.bsky.app/xrpc/app.bsky.video.uploadVideo with an Authorization: Bearer service token (not the session's DPoP token). The token is minted by the user's PDS via com.atproto.server.getServiceAuth with the PDS DID as the audience and com.atproto.repo.uploadBlob as the lexicon, because the service deposits the processed blob into your PDS on your behalf. "getVideoUploadLimits( )" and "getVideoJobStatus( $jobId )" use the video service host too.

Unlike "uploadVideo( $video )", which uploads a finished blob for embedding, this starts a server-side processing job. The recommended flow from https://bsky.network/docs/about-bluesky-content/video is: create a service token for com.atproto.repo.uploadBlob, upload to the video service, poll the job for its blob, then post. The uploadVideo reply is the job status: if it already carries a blob use it directly, otherwise poll "getVideoJobStatus( $jobId )" until blob is present. If the video was already processed the service reports an already_exists error (HTTP 409), and the returned job may need to be polled to obtain its blob. Note that Bluesky-hosted accounts must verify their email before uploading video, and daily video limits apply (see "getVideoUploadLimits( )").

Drafts

Methods in this section manage private post drafts (stored server-side, requiring auth).

getDrafts( [...] )

$bsky->getDrafts();

Get views of the user's drafts. Returns the drafts arrayref.

createDraft( $draft )

$bsky->createDraft( { posts => [ { text => 'Unfinished thought...' } ] } );

Insert a draft. Returns the creation response, including the draft id.

updateDraft( $draft )

$bsky->updateDraft( { id => $id, draft => { posts => [ ... ] } } );

Update a draft. Updates to unknown IDs are silently ignored.

deleteDraft( $id )

$bsky->deleteDraft( $id );

Delete a draft by ID.

Contacts

Methods in this section deal with secure contact-import matching (all require auth).

getContactMatches( [...] )

$bsky->getContactMatches();

Return matched contacts (mutually imported, excluding dismissed matches). Returns the matches arrayref.

getContactSyncStatus( )

$bsky->getContactSyncStatus();

Get the user's current contact import status.

importContacts( ... )

$bsky->importContacts( token => $token, contacts => [ '+12125550123' ] );

Import contacts for secure matching. $token comes from "verifyPhone( $phone, $code )". Returns matched contacts with their input indexes. See https://docs.bsky.app/blog/contact-import-rfc.

dismissContactMatch( $subject )

$bsky->dismissContactMatch( 'did:plc:...' );

Dismiss a contact match so it no longer appears, even on re-import.

removeContactData( )

$bsky->removeContactData();

Remove all stored contact-matching hashes, matches, and sync status.

startPhoneVerification( $phone )

$bsky->startPhoneVerification( '+12125550123' );

Start a phone verification flow; the phone receives an SMS code for "verifyPhone( $phone, $code )".

verifyPhone( $phone, $code )

my $token = $bsky->verifyPhone( '+12125550123', '123456' );

Verify control over a phone number and start a contact import session. Returns a single-use token for "importContacts( ... )".

Age Assurance

Methods in this section deal with Age Assurance.

beginAgeAssurance( ... )

$bsky->beginAgeAssurance( email => 'me@example.com', language => 'en', countryCode => 'US' );

Initiate Age Assurance for the account. Requires email, language, and countryCode.

getAgeAssuranceConfig( )

$bsky->getAgeAssuranceConfig();

Return Age Assurance configuration for use on the client.

getAgeAssuranceState( ... )

$bsky->getAgeAssuranceState( countryCode => 'US' );

Return the server-computed Age Assurance state. countryCode is required.

Utilities and Upload Helpers

Helper methods used internally by "createPost( ... )" and available for advanced use.

parse_mentions( $text )

Returns a list of { start => ..., end => ..., handle => ... } spans for @-mentions in $text.

parse_urls( $text )

Returns a list of { start => ..., end => ..., url => ... } spans for URLs in $text.

parse_tags( $text )

Returns a list of { start => ..., end => ..., tag => ... } spans for #-tags in $text.

parse_facets( $text )

Resolves mentions, URLs, and tags in $text into an array of app.bsky.richtext.facet structures.

Facet indexes are byte offsets into the UTF-8 encoding (inclusive start, exclusive end), not character offsets; this module enables bytes internally so the offsets come out correctly. Facets must not overlap. See https://bsky.network/docs/about-bluesky-content/post-richtext.

parse_uri( $uri )

my $parts = $bsky->parse_uri( 'at://did:plc:.../app.bsky.feed.post/3l6oveex3ii2l' );

Returns { repo => ..., collection => ..., rkey => ... } for an AT-URI.

getReplyRefs( $parent_uri )

Fetches parent (and root, if the parent is itself a reply) records and returns { root => ..., parent => ... } strong refs suitable for app.bsky.feed.post replies.

uploadFile( $bytes_or_path, [ $mime_type ] )

Reads a file, Path::Tiny object, or raw bytes and uploads it via com.atproto.repo.uploadBlob using HTTP::Tiny directly. Returns the blob on success.

uploadImages( @images )

Uploads up to 4 images (paths, raw bytes, Path::Tiny objects, or { image => ..., alt => ..., mime => ..., aspectRatio => { width => ..., height => ... } } hashrefs) and returns an app.bsky.embed.images structure.

uploadGallery( @items )

Uploads gallery items (same accepted forms as "uploadImages( @images )") and returns an app.bsky.embed.gallery structure. Each item becomes an app.bsky.embed.gallery#image and may carry an aspectRatio.

uploadVideoCaption( $lang, $caption )

Uploads a WebVTT caption file and returns an app.bsky.embed.video#caption structure.

uploadVideo( $video )

Uploads a video blob (path, raw bytes, Path::Tiny object, or hashref with video, alt, mime, aspectRatio, presentation, and captions) and returns an app.bsky.embed.video structure.

This uploads a finished video blob straight to the PDS. To run the video service's server-side processing job instead, use "uploadVideoJob( $bytes_or_path, [ $mime_type, %opts ] )" and then "videoEmbed( $blob, %opts )".

videoEmbed( $blob, %opts )

Builds an app.bsky.embed.video structure from an already-uploaded blob, typically the processed blob returned by "getVideoJobStatus( $jobId )" after "uploadVideoJob( $bytes_or_path, [ $mime_type, %opts ] )". Recognized %opts are alt, aspectRatio, presentation (default or gif), and captions (either { lang => file } or [ { lang => ..., file => ... } ]). Caption files are uploaded for you.

my $job    = $bsky->uploadVideoJob( 'clip.mp4', 'video/mp4', did => $bsky->did, name => 'clip.mp4' );
my $status = $bsky->getVideoJobStatus( $job->{jobId} );
my $embed  = $bsky->videoEmbed( $status->{blob},
    alt          => 'A cat playing a piano',
    aspectRatio  => { width => 1280, height => 720 },
    presentation => 'default',
    captions     => { en => 'captions.en.vtt' },
);
$bsky->createPost( text => 'Music!', embed => $embed );

getEmbedRef( $uri )

Fetches a record and returns an app.bsky.embed.record structure linking to it.

fetch_embed_url_card( $url )

Fetches $url, extracts title/description/thumbnail, and returns an app.bsky.embed.external structure.

See Also

At - AT Protocol library

App::bsky - Bluesky client on the command line

https://docs.bsky.app/docs/api/

Perl Starter Pack

I've created a starter pack of Perl folks on Bluesky.

Follow it at https://bsky.app/starter-pack/sankorobinson.com/3lk3xd5utq52s and get in touch to have yourself added.

LICENSE

Copyright (C) Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License 2. Other copyrights, terms, and conditions may apply to data transmitted through this module.

AUTHOR

Sanko Robinson - https://github.com/sanko