bluesky 0.7.6 copy "bluesky: ^0.7.6" to clipboard
bluesky: ^0.7.6 copied to clipboard

outdated

The easiest and powerful Dart/Flutter library for Bluesky Social.

bluesky

The Easiest and Powerful Dart/Flutter Library for Bluesky Social 🎯


GitHub Sponsor GitHub Sponsor

pub package Dart SDK Version Test/Analyzer codecov Issues Pull Requests Stars Contributors Last Commits License Contributor Covenant


1. Guide 🌎 #

This library provides the easiest way to use Bluesky Social in Dart and Flutter apps.

Show some ❀️ and star the repo to support the project.

bluesky is developed on the basis of the atproto package.

However, the atproto package is highly integrated into this package, so you do not need to be aware of atproto when you use bluesky.

If you want to use atproto-only features, please check atproto.

Check official page too!

1.1. Features πŸ’Ž #

  • βœ… Zero Dependency
  • βœ… Supports Powerful Built-In Retry using Exponential BackOff And Jitter
  • βœ… Supports All Major Endpoints for app.bsky.*
  • βœ… Well Documented and Well Tested
  • βœ… Supports Powerful Firehose API
  • βœ… 100% Null Safety
  • βœ… Built In atproto Features

1.2. Getting Started ⚑ #

1.2.1. Install Library #

With Dart:

 dart pub add bluesky

Or With Flutter:

 flutter pub add bluesky

1.2.2. Import #

import 'package:bluesky/bluesky.dart';

1.2.3. Implementation #

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  try {
    final bluesky = bsky.Bluesky.fromSession(
      //! First you need to establish session with ATP server.
      await _session,

      //! The default is `bsky.social`
      service: 'SERVICE_NAME',

      //! Automatic retry is available when server error or network error occurs
      //! when communicating with the API.
      retryConfig: bsky.RetryConfig(
        maxAttempts: 5,
        jitter: bsky.Jitter(
          minInSeconds: 2,
          maxInSeconds: 5,
        ),
        onExecute: (event) => print(
          'Retry after ${event.intervalInSeconds} seconds...'
          '[${event.retryCount} times]',
        ),
      ),

      //! The default timeout is 30 seconds.
      timeout: Duration(seconds: 20),
    );

    //! Let's get home timeline!
    final feeds = await bluesky.feeds.findTimeline(
      limit: 10,
    );

    print(feeds);

    //! Let's post cool stuff!
    final createdRecord = await bluesky.feeds.createPost(
      text: 'Hello, Bluesky!',
    );

    print(createdRecord);

    //! And delete it.
    await bluesky.repositories.deleteRecord(
      uri: createdRecord.data.uri,
    );

    //! You can use Stream API easily.
    final subscription = await bluesky.sync.subscribeRepoUpdates();

    subscription.data.stream.listen((event) {
      event.when(
        //! You can handle commit events very easily
        //! with RepoCommitAdaptor.
        commit: bsky.RepoCommitAdaptor(
          //! Create events.
          onCreatePost: (data) => data.record,
          onCreateLike: print,

          //! Update events.
          onUpdateProfile: print,

          //! Delete events.
          onDeletePost: print,
        ).execute,
        handle: print,
        migrate: print,
        tombstone: print,
        info: print,
        unknown: print,
      );
    });
  } on bsky.UnauthorizedException catch (e) {
    print(e);
  } on bsky.XRPCException catch (e) {
    print(e);
  }
}

Future<bsky.Session> get _session async {
  final session = await bsky.createSession(
    service: 'SERVICE_NAME', //! The default is `bsky.social`
    identifier: 'YOUR_HANDLE_OR_EMAIL', //! Like `shinyakato.bsky.social`
    password: 'YOUR_PASSWORD',
  );

  return session.data;
}

1.3. Supported Lexicons πŸ‘€ #

1.3.1. Actors Service #

Lexicon Method Name Auth Required
GET app.bsky.actor.searchActors searchActors βœ…
GET app.bsky.actor.getProfile findProfile βœ…
GET app.bsky.actor.getProfiles findProfiles βœ…
GET app.bsky.actor.getSuggestions findSuggestions βœ…
GET app.bsky.actor.searchActorsTypeahead searchTypeahead βœ…
POST app.bsky.actor.profile updateProfile βœ…
GET app.bsky.actor.getPreferences findPreferences βœ…
POST app.bsky.actor.putPreferences updatePreferences βœ…

1.3.2. Feeds Service #

Lexicon Method Name Auth Required
POST app.bsky.feed.post createPost βœ…
POST com.atproto.repo.applyWrites createPosts βœ…
GET app.bsky.feed.getTimeline findTimeline βœ…
POST app.bsky.feed.repost createRepost βœ…
POST com.atproto.repo.applyWrites createReposts βœ…
POST app.bsky.feed.like createLike βœ…
POST com.atproto.repo.applyWrites createLikes βœ…
GET app.bsky.feed.getAuthorFeed findFeed βœ…
GET app.bsky.feed.getLikes findLikes βœ…
GET app.bsky.feed.getRepostedBy findRepostedBy βœ…
GET app.bsky.feed.getPostThread findPosThread βœ…
GET app.bsky.feed.getPosts findPosts βœ…
POST app.bsky.feed.generator createGenerator βœ…
POST com.atproto.repo.applyWrites createGenerators βœ…
GET app.bsky.feed.getActorFeeds findActorFeeds βœ…
GET app.bsky.feed.getFeedGenerator findGenerator βœ…
GET app.bsky.feed.getFeedGenerators findGenerators βœ…
GET app.bsky.feed.getFeed findCustomFeed βœ…
GET app.bsky.feed.getFeedSkeleton findFeedSkeleton βœ…
GET app.bsky.feed.describeFeedGenerator findGeneratorInfo βœ…

1.3.3. Notifications Service #

Lexicon Method Name Auth Required
GET app.bsky.notification.listNotifications findNotifications βœ…
GET app.bsky.notification.getUnreadCount findUnreadCount βœ…
POST app.bsky.notification.updateSeen updateNotificationsAsRead βœ…

1.3.4. Graphs Service #

Lexicon Method Name Auth Required
POST app.bsky.graph.follow createFollow βœ…
POST com.atproto.repo.applyWrites createFollows βœ…
GET app.bsky.graph.getFollows findFollows βœ…
GET app.bsky.graph.getFollowers findFollowers βœ…
POST app.bsky.graph.muteActor createMute βœ…
POST app.bsky.graph.unmuteActor deleteMute βœ…
GET app.bsky.graph.getMutes findMutes βœ…
GET app.bsky.graph.getBlocks findBlocks βœ…
POST app.bsky.graph.block createBlock βœ…
POST com.atproto.repo.applyWrites createBlocks βœ…
POST com.atproto.repo.list createList βœ…
POST com.atproto.repo.applyWrites createLists βœ…
POST com.atproto.repo.getLists findLists βœ…
POST com.atproto.repo.getList findList βœ…
POST com.atproto.repo.listitem createListItem βœ…
POST com.atproto.repo.applyWrites createListItems βœ…
GET app.bsky.graph.getListMutes findMutingLists βœ…
POST app.bsky.graph.muteActorList createMuteActorList βœ…
POST app.bsky.graph.unmuteActorList deleteMuteActorList βœ…

1.3.5. Unspecced Service #

Lexicon Method Name Auth Required
GET app.bsky.unspecced.getPopular findPopularFeed βœ…
GET app.bsky.unspecced.getPopularFeedGenerators findPopularFeedGenerators βœ…

1.3.6. Servers Service #

Lexicon Method Name Auth Required
POST com.atproto.server.createSession createSession βœ…
POST com.atproto.server.refreshSession refreshSession ❌
GET com.atproto.server.getSession findCurrentSession βœ…
POST com.atproto.server.createAccount createAccount ❌
POST com.atproto.server.requestDeleteAccount requestDeleteAccount βœ…
POST com.atproto.server.deleteAccount deleteAccount βœ…
POST com.atproto.server.createInviteCode createInviteCode βœ…
POST com.atproto.server.createInviteCodes createInviteCodes βœ…
GET com.atproto.server.getAccountInviteCodes findInviteCodes βœ…
POST com.atproto.server.requestPasswordReset requestPasswordReset βœ…
POST com.atproto.server.resetPassword updatePassword βœ…
POST com.atproto.server.createAppPassword createAppPassword βœ…
POST com.atproto.server.revokeAppPassword deleteAppPassword βœ…
GET com.atproto.server.listAppPasswords findAppPasswords βœ…
GET com.atproto.server.describeServer findServerInfo ❌

1.3.7. Identity Service #

Lexicon Method Name Auth Required
GET com.atproto.identity.resolveHandle findDID ❌
POST com.atproto.identity.updateHandle updateHandle βœ…

1.3.8. Repository Service #

Lexicon Method Name Auth Required
POST com.atproto.repo.createRecord createRecord βœ…
GET com.atproto.repo.getRecord findRecord βœ…
GET com.atproto.repo.listRecords findRecords βœ…
POST com.atproto.repo.deleteRecord deleteRecord βœ…
POST com.atproto.repo.uploadBlob uploadBlob βœ…
GET com.atproto.repo.describeRepo findRepoInfo ❌
POST com.atproto.repo.applyWrites updateBulk βœ…
POST com.atproto.repo.applyWrites createRecords βœ…
POST com.atproto.repo.applyWrites updateRecords βœ…
POST com.atproto.repo.applyWrites deleteRecords βœ…
POST com.atproto.repo.rebaseRepo rebaseRepo βœ…

1.3.9. Moderation Service #

Lexicon Method Name Auth Required
POST com.atproto.moderation.createReport createReport βœ…

1.3.10. Sync Service #

Lexicon Method Name Auth Required
com.atproto.sync.subscribeRepos subscribeRepos ❌
GET com.atproto.sync.getHead findRepoHead ❌
GET com.atproto.sync.getRepo findRepoCommits ❌
GET com.atproto.sync.getCheckout findRepoCheckout ❌
GET com.atproto.sync.getCommitPath findRepoCommitPaths ❌
GET com.atproto.sync.getBlocks findRepoBlocks ❌
GET com.atproto.sync.getRecord findRecord ❌
GET com.atproto.sync.listRepos findRepos ❌

1.4. Tips πŸ„ #

1.4.1. Method Names #

bluesky uses the following standard prefixes depending on endpoint characteristics. So it's very easy to find the method corresponding to the endpoint you want to use!

Prefix Description
find This prefix is attached to endpoints that reference post etc.
search This prefix is attached to endpoints that perform extensive searches.
subscribe This prefix is attached to endpoints with high-performance streaming.
create This prefix is attached to the endpoint performing the create state.
refresh This prefix is attached to the endpoint performing the refresh state.
delete This prefix is attached to the endpoint performing the delete state.
update This prefix is attached to the endpoint performing the update state.
upload This prefix is attached to the endpoint performing the upload contents.
request This prefix is attached to the endpoint performing the request via email.
rebase This prefix is attached to the endpoint performing the rebase repo.

1.4.2. Create Session #

First, in order for us to enjoy using this library programmatically in Bluesky, we need to send a request to the ATP server and establish a session.

Once this session is established, you will have an access token to use Bluesky's API.

You can easily establish a session with the following process. Prepare the name of the service you wish to establish a session with and your credentials.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final session = await bsky.createSession(
    service: 'SERVICE_NAME', //! The default is `bsky.social`
    identifier: 'YOUR_HANDLE_OR_EMAIL', //! Like `shinyakato.bsky.social`
    password: 'YOUR_PASSWORD',
  );

  print(session);
}

Once you have established a session, you can now create an instance of the Bluesky object. You can easily create an instance of a Bluesky object from a previously established Session object.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final session = await bsky.createSession(
    service: 'SERVICE_NAME', //! The default is `bsky.social`
    identifier: 'YOUR_HANDLE_OR_EMAIL', //! Like `shinyakato.bsky.social`
    password: 'YOUR_PASSWORD',
  );

  final bluesky = bsky.Bluesky.fromSession(
    session.data,
  );
}

Or, you can do as follows:

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final session = await bsky.createSession(
    service: 'SERVICE_NAME', //! The default is `bsky.social`
    identifier: 'YOUR_HANDLE_OR_EMAIL', //! Like `shinyakato.bsky.social`
    password: 'YOUR_PASSWORD',
  );

  final bluesky = bsky.Bluesky(
    did: session.data.did,
    accessJwt: session.data.accessJwt,
  );
}

1.4.3. Null Parameter at Request #

In this library, parameters that are not required at request time, i.e., optional parameters, are defined as nullable. However, developers do not need to be aware of the null parameter when sending requests when using this library.

It means the parameters specified with a null value are safely removed and ignored before the request is sent.

For example, arguments specified with null are ignored in the following request.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final bluesky = bsky.Bluesky.fromSession(await _session);
  //! Let's get home timeline!
  final feeds = await bluesky.feeds.getHomeTimeline(
    algorithm: null,
    limit: null,
  );

  print(feeds);
}

Future<bsky.Session> get _session async {
  final session = await bsky.createSession(
    identifier: 'YOUR_HANDLE_OR_EMAIL',
    password: 'YOUR_PASSWORD',
  );

  return session.data;
}

1.4.4. Change the Timeout Duration #

The library specifies a default timeout of 10 seconds for all API communications.

However, there may be times when you wish to specify an arbitrary timeout duration. If there is such a demand, an arbitrary timeout duration can be specified as follows.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final bluesky = bsky.Bluesky(
    did: 'YOUR_DID',
    accessJwt: 'YOUR_TOKEN',

    //! The default timeout is 30 seconds.
    timeout: Duration(seconds: 20),
  );
}

1.4.5. Automatic Retry #

Due to the nature of this library's communication with external systems, timeouts may occur due to inevitable communication failures or temporary crashes of the server to which requests are sent.

When such timeouts occur, an effective countermeasure in many cases is to send the request again after a certain interval. And bluesky provides an automatic retry feature as a solution to this problem.

Also, errors subject to retry are as follows.

  • When the status code of the response returned from ATP server is 500 or 503.
  • When the network is temporarily lost and a SocketException is thrown.
  • When communication times out temporarily and a TimeoutException is thrown

1.4.5.1. Exponential Backoff and Jitter

Although the algorithm introduced earlier that exponentially increases the retry interval is already powerful, some may believe that it is not yet sufficient to distribute the sensation of retries. It's more distributed than equally spaced retries, but retries still occur at static intervals.

This problem can be solved by adding a random number called Jitter, and this method is called the Exponential Backoff and Jitter algorithm. By adding a random number to the exponentially increasing retry interval, the retry interval can be distributed more flexibly.

Similar to the previous example, bluesky can be implemented as follows.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final bluesky = bsky.Bluesky(
    did: 'YOUR_DID',
    accessJwt: 'YOUR_TOKEN',

    //! Add these lines.
    retryConfig: bsky.RetryConfig(
      maxAttempts: 3,
    ),
  );
}

In the above implementation, the interval increases exponentially for each retry count with jitter. It can be expressed by next formula.

(2 ^ retryCount) + jitter(Random Number between 0 ~ 3)

1.4.5.2. Do Something on Retry

It would be useful to output logging on retries and a popup notifying the user that a retry has been executed. So bluesky provides callbacks that can perform arbitrary processing when retries are executed.

It can be implemented as follows.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final bluesky = bsky.Bluesky(
    did: 'YOUR_DID',
    accessJwt: 'YOUR_TOKEN',

    retryConfig: bsky.RetryConfig(
      maxAttempts: 3,

      //! Add this line.
      onExecute: (event) => print('Retrying... ${event.retryCount} times.'),
    ),
  );
}

The RetryEvent passed to the callback contains information on retries.

1.4.6. Thrown Exceptions #

bluesky provides a convenient exception object for easy handling of exceptional responses and errors returned from AT Protocol.

Exception Description
XRPCException Parent class for all the following Exceptions.
UnauthorizedException Thrown when authentication fails with the specified access token.
RateLimitExceededException Thrown when the rate limit is reached.
InvalidRequestException Thrown when request parameters are invalid.
InternalServerErrorException Thrown when a failure occurs on the ATP server.

Also, all of the above exceptions thrown from the bluesky process extend XRPCException. This means that you can take all exceptions as XRPCException or handle them as certain exception types, depending on the situation.

However note that, if you receive an individual type exception, be sure to define the process so that the individual exception type is cached before XRPCException. Otherwise, certain type exceptions will also be caught as XRPCException.

Therefore, if you need to catch a specific type of exception in addition to XRPCException, be sure to catch XRPCException in the bottom catch clause as in the following example.

import 'package:bluesky/bluesky.dart' as bsky;

Future<void> main() async {
  final bluesky = bsky.Bluesky(
    did: 'YOUR_DID',
    accessJwt: 'YOUR_TOKEN',
  );

  try {
    final response = await bluesky.feeds.getHomeTimeline();

    print(response);
  } on bsky.UnauthorizedException catch (e) {
    print(e);
  } on bsky.XRPCException catch (e) {
    print(e);
  }
}

1.5. Contribution πŸ† #

If you would like to contribute to bluesky, please create an issue or create a Pull Request.

There are many ways to contribute to the OSS. For example, the following subjects can be considered:

  • There are request parameters or response fields that are not implemented.
  • Documentation is outdated or incomplete.
  • Have a better way or idea to achieve the functionality.
  • etc...

You can see more details from resources below:

Or you can create a discussion if you like.

Feel free to join this development, diverse opinions make software better!

1.6. Support ❀️ #

The simplest way to show us your support is by giving the project a star at GitHub and Pub.dev.

You can also support this project by becoming a sponsor on GitHub:

You can also show on your repository that your app is made with bluesky by using one of the following badges:

Powered by bluesky Powered by bluesky Powered by bluesky

[![Powered by bluesky](https://img.shields.io/badge/Powered%20by-bluesky-00acee.svg)](https://github.com/myConsciousness/atproto.dart)
[![Powered by bluesky](https://img.shields.io/badge/Powered%20by-bluesky-00acee.svg?style=flat-square)](https://github.com/myConsciousness/atproto.dart)
[![Powered by bluesky](https://img.shields.io/badge/Powered%20by-bluesky-00acee.svg?style=for-the-badge)](https://github.com/myConsciousness/atproto.dart)

1.7. License πŸ”‘ #

All resources of bluesky is provided under the BSD-3 license.

Copyright 2023 Shinya Kato. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided the conditions.

Note
License notices in the source are strictly validated based on .github/header-checker-lint.yml. Please check header-checker-lint.yml for the permitted standards.

1.8. More Information 🧐 #

bluesky was designed and implemented by Shinya Kato (@myConsciousness).

30
likes
0
points
6.05k
downloads

Publisher

verified publisheratprotodart.com

Weekly Downloads

The easiest and powerful Dart/Flutter library for Bluesky Social.

Homepage
Repository (GitHub)
View/report issues

Topics

#atproto #bluesky #api

Funding

Consider supporting this project:

github.com

License

unknown (license)

Dependencies

atproto, atproto_core, freezed_annotation, json_annotation

More

Packages that depend on bluesky