misskey_client 1.0.0-beta.9
misskey_client: ^1.0.0-beta.9 copied to clipboard
A pure Dart client library for the Misskey API. Covers 26 API domains with token-based auth, automatic retry, and structured error handling.
日本語 | 简体中文 | Deutsch | Français | 한국어
misskey_client #
A pure Dart client library for the Misskey API. Provides typed access to 26 API domains with built-in authentication, retry logic, and structured error handling.
Beta: API implementation is complete but test coverage is minimal. Response models and method signatures may change based on test findings. See the changelog for details.
Features #
- Covers 26 Misskey API domains (Notes, Drive, Users, Channels, Chat, and more)
- Token-based authentication via a pluggable
TokenProvidercallback - Automatic retry with configurable maximum attempts
- Sealed exception hierarchy for exhaustive error handling
- Strongly typed request and response models generated with
json_serializable - Integrated Streaming API with typed channels, events, and automatic reconnection
- Drive helpers for auto-pagination, bulk moves, deduplicated batch uploads, and recursive folder operations with per-item results
- Configurable logging through a swappable
Loggerinterface - Pure Dart — no Flutter dependency required
Installation #
Add the package to your pubspec.yaml:
dependencies:
misskey_client: ^1.0.0-beta.9
Then run:
dart pub get
Quick Start #
import 'package:misskey_client/misskey_client.dart';
void main() async {
final client = MisskeyClient(
config: MisskeyClientConfig(
baseUrl: Uri.parse('https://misskey.example.com'),
timeout: Duration(seconds: 10),
maxRetries: 3,
),
// Supply your access token. The callback may be async.
tokenProvider: () => 'YOUR_ACCESS_TOKEN',
);
// Fetch the authenticated user's own notes
final notes = await client.notes.getTimeline();
for (final note in notes) {
print(note.text);
}
}
API Overview #
MisskeyClient exposes the following properties, each covering a distinct area of the Misskey API:
| Property | Description |
|---|---|
account |
Account and profile management, registry, 2FA, webhooks |
accountLifecycle |
Sign-up validation, password reset, email verification |
announcements |
Server announcements |
antennas |
Antenna (keyword-based feed) management |
ap |
ActivityPub utilities |
blocking |
User blocking |
channels |
Channels and channel mutes |
charts |
Statistics charts |
chat |
Chat rooms and messages |
clips |
Clip collections |
drive |
Drive (file storage), files, folders, stats; helpers for listing all items, bulk moves, batch uploads, folder trees, and recursive deletion |
federation |
Federated instance information |
flash |
Flash (Play) scripts |
following |
Following and follow requests |
gallery |
Gallery posts |
hashtags |
Hashtag search and trends |
invite |
Invite codes |
meta |
Server metadata |
mute |
User muting |
notes |
Notes, reactions, polls, search, timeline |
notifications |
Notifications |
pages |
Pages |
renoteMute |
Renote muting |
roles |
Role assignments |
sw |
Push notifications (Service Worker) |
streaming |
Real-time timelines, notifications, and captured note updates |
users |
User search, lists, relations, achievements |
Server compatibility #
Servers can lag behind current Misskey or run a fork with a different API surface. Prefer runtime endpoint enumeration over comparing Meta.version: fork version strings are not necessarily comparable with Misskey releases, while /api/endpoints reports what that server actually advertises.
final canCreateDrafts = await client.meta.isEndpointAvailable(
endpoint: 'notes/drafts/create', // No /api/ prefix.
);
if (canCreateDrafts) {
// Show or call the draft feature.
}
The endpoint list is cached in memory. Pass refresh: true to isEndpointAvailable() or getEndpoints() after a server upgrade or when you need a fresh snapshot. Enumeration is a preflight hint, not a guarantee: the server can change after the check, so still handle MisskeyNotFoundException when calling the endpoint. If /api/endpoints itself is unavailable or fails on a fork, fall back to calling the desired API and handling its 404; a 404 alone may be ambiguous between an absent endpoint and an absent resource.
hasMetaKey('features.x') only checks whether a metadata key exists. It returns true even when that key's value is false, so do not use metadata key presence as a substitute for endpoint detection.
Streaming API #
The lazily created client.streaming connection shares the client's server, token provider, and logger. Subscribe with a typed MisskeyStreamingChannel and choose the level that fits your application: decoded notes and notifications, typed events, or lossless messages.
Future<void> streamHomeTimeline() async {
final client = MisskeyClient(
config: MisskeyClientConfig(
baseUrl: Uri.parse('https://misskey.example.com'),
),
tokenProvider: () => 'YOUR_ACCESS_TOKEN',
streamingConfig: MisskeyStreamingConfig(maxReconnectAttempts: 5),
);
await client.streaming.connect();
final home = await client.streaming.subscribe(
const MisskeyStreamingChannel.homeTimeline(
withRenotes: true,
withFiles: false,
),
);
final notesSubscription = home.notes.listen((note) {
print(note.text);
});
final eventsSubscription = home.events.listen((event) {
if (event is MisskeyNoteReactedEvent) {
print('${event.noteId}: ${event.reaction}');
}
});
// Capture a known note to receive reaction, deletion, and poll updates.
home.captureNote('NOTE_ID');
await notesSubscription.cancel();
await eventsSubscription.cancel();
home.uncaptureNote('NOTE_ID');
await home.unsubscribe();
await client.dispose();
}
Use subscribeRaw(channel: ..., params: ...) for fork-specific channels. It returns the same subscription handle, including the messages stream. connect(), disconnect(), and reconnect() control a reusable connection; dispose() is terminal. Connection states are available through state and stateChanges, while asynchronous failures are reported through errors.
Authentication #
Pass a TokenProvider callback when constructing the client. The callback returns FutureOr<String?>, so both synchronous and asynchronous token sources are supported:
// Synchronous token
final client = MisskeyClient(
config: config,
tokenProvider: () => secureStorage.readSync('token'),
);
// Asynchronous token
final client = MisskeyClient(
config: config,
tokenProvider: () async => await secureStorage.read('token'),
);
Endpoints that require authentication inject the token automatically. Endpoints that are optionally authenticated attach the token when one is available.
Error Handling #
API and transport exceptions extend the sealed class MisskeyClientException, allowing exhaustive pattern matching:
try {
final user = await client.users.show(userId: 'abc123');
} on MisskeyUnauthorizedException {
// 401 — token invalid or missing
} on MisskeyForbiddenException {
// 403 — operation not permitted
} on MisskeyNotFoundException {
// 404 — resource not found
} on MisskeyRateLimitException catch (e) {
// 429 — rate limited; check e.retryAfter
} on MisskeyValidationException {
// 422 — invalid request body
} on MisskeyServerException {
// 5xx — server-side error
} on MisskeyNetworkException {
// Timeout, connection refused, etc.
}
Helper APIs can additionally throw ArgumentError for invalid arguments (before any request), StateError for unmet preconditions (such as drive.uploadFromUrlAndWait() without a connected main streaming subscription), and DriveFolderAmbiguousException, which is outside the sealed hierarchy. Once batch helpers that change many items have started making changes, individual operation failures are recorded in a MisskeyBatchResult instead of being thrown; an error thrown by an onProgress callback while reporting a settled item is rethrown after in-flight work finishes, without rolling back completed changes.
Logging #
Enable the built-in stdout logger via MisskeyClientConfig.enableLog, or supply a custom Logger implementation:
class MyLogger implements Logger {
@override void debug(String message) { /* ... */ }
@override void info(String message) { /* ... */ }
@override void warn(String message) { /* ... */ }
@override void error(String message, [Object? error, StackTrace? stackTrace]) { /* ... */ }
}
final client = MisskeyClient(
config: MisskeyClientConfig(baseUrl: Uri.parse('https://misskey.example.com')),
logger: MyLogger(),
);
Migrating from misskey_api_core #
API mapping #
| misskey_api_core | misskey_client |
|---|---|
MisskeyHttpClient(config: ..., tokenProvider: ...) |
MisskeyClient(config: ..., tokenProvider: ...) |
MisskeyApiConfig(baseUrl: ...) |
MisskeyClientConfig(baseUrl: ...) |
http.send<T>('/emojis', ...) |
The corresponding typed method, such as client.meta.getEmojis() |
MetaClient(http).getMeta() |
client.meta.getMeta() |
MisskeyApiException |
The sealed hierarchy containing MisskeyApiException, MisskeyUnauthorizedException, MisskeyForbiddenException, MisskeyRateLimitException, and others |
RequestOptions(authRequired: false) |
Handled internally by typed methods; callers do not need to specify it |
Logger / FunctionLogger |
Classes with the same names |
kReleaseMode / kDebugMode |
Not part of the public API; see below |
Migrating from misskey_api_kit #
misskey_api_kit was an unpublished predecessor. Remove that dependency and use a single MisskeyClient instead of a separate MisskeyApiKitClient instance.
- Replace the
account,notes,notifications,channels, andusersentry points fromMisskeyApiKitClientwith the properties of the same names onMisskeyClient.
This is not a drop-in replacement: some methods have been renamed, and many responses that were raw Map<String, dynamic> values now use typed models, although some APIs still return raw maps. Migrate each call against the API reference.
Exception name collision #
Both packages define MisskeyApiException, but the classes have different contents and no inheritance relationship. The misskey_api_core version is a simple class, while the misskey_client version extends MisskeyClientException and requires a statusCode. While importing both packages during migration, use a prefix to avoid the collision:
import 'package:misskey_api_core/misskey_api_core.dart' as core;
Build mode constants #
misskey_api_core exported kReleaseMode and kDebugMode, but misskey_client does not. They are general-purpose utilities unrelated to Misskey. Flutter applications should import them from package:flutter/foundation.dart; pure Dart applications can use bool.fromEnvironment('dart.vm.product'). To control client logging, pass the value through MisskeyClientConfig.enableLog, for example enableLog: kDebugMode.
Low-level HTTP access #
The low-level equivalent of MisskeyHttpClient.send<T>() is not public. misskey_client covers 26 API domains, so use its typed methods. If an endpoint you need is missing, please report it in a GitHub issue so it can be added to the typed API.
Migrating from misskey_streaming #
Streaming is now integrated into misskey_client. See the migration guide for dependency, configuration, subscription, event, note capture, and lifecycle mappings from the standalone misskey_streaming package.
Documentation #
- API reference: https://librarylibrarian.github.io/misskey_client/
- Endpoint support policy
- pub.dev page: https://pub.dev/packages/misskey_client
- GitHub: https://github.com/LibraryLibrarian/misskey_client
License #
See LICENSE.