> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# BlueskyManager und HasShortHand

> Erläutert die Architektur des BlueskyManager als konkrete Implementierung hinter der Bluesky-Facade sowie die Shortcut-Methoden des Traits HasShortHand.

## Überblick

Die Facade `Bluesky` ist ein dünner Wrapper um `BlueskyManager`. `BlueskyManager` verwaltet mehrere Agents, und der Trait `HasShortHand` bündelt Shortcuts für die am häufigsten verwendeten Methoden.

## Architektur

```mermaid theme={null}
graph TD
    A["Bluesky Facade<br>(Revolution\\Bluesky\\Facades\\Bluesky)"] --> B["BlueskyManager<br>(Factory-Interface)"]
    B --> C["HasShortHand-Trait<br>Shortcut-Methoden"]
    B --> D["Conditionable-Trait<br>when() / unless()"]
    B --> E["Macroable-Trait<br>macro() / mixin()"]
    B --> F{"Agent"}
    F --> G["LegacyAgent<br>(App Password)"]
    F --> H["OAuthAgent<br>(OAuth)"]
    G --> I["LegacySession"]
    H --> J["OAuthSession"]
```

Der `BlueskyServiceProvider` registriert `Factory::class` als `BlueskyManager` als Scoped Singleton (eine Instanz pro Request).

```php theme={null}
// BlueskyServiceProvider::register()
$this->app->scoped(Factory::class, BlueskyManager::class);
```

Der `getFacadeAccessor()` der Facade löst dieses Binding auf.

```php theme={null}
// Facades/Bluesky.php
protected static function getFacadeAccessor(): string
{
    return Factory::class;
}
```

## Kernmethoden des BlueskyManager

Die vom `BlueskyManager` selbst definierten Kernmethoden sind die folgenden. Es hilft beim Verständnis der internen Struktur, sie klar von den Methoden des Traits `HasShortHand` abzugrenzen.

### Authentifizierung

| Methode                                                                | Beschreibung                                                          |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `login(string $identifier, string $password, ?string $service = null)` | Authentifiziert per App Password und setzt einen `LegacyAgent`        |
| `withToken(?AbstractSession $token)`                                   | Übergibt eine `OAuthSession` oder `LegacySession` und setzt den Agent |
| `check(): bool`                                                        | Prüft, ob authentifiziert wurde (inklusive Token-Gültigkeit)          |
| `refreshSession()`                                                     | Aktualisiert das Token                                                |
| `logout()`                                                             | Setzt den Agent zurück                                                |

### Agent-Operationen

| Methode                    | Beschreibung                                                                           |
| -------------------------- | -------------------------------------------------------------------------------------- |
| `agent(): ?Agent`          | Gibt den aktuellen Agent zurück                                                        |
| `withAgent(?Agent $agent)` | Setzt den Agent direkt                                                                 |
| `assertDid(): string`      | Gibt die authentifizierte DID zurück; wirft eine Exception, wenn nicht authentifiziert |

### HTTP-Client

| Methode                                                                                          | Beschreibung                                                               |
| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| `client(bool $auth = true): AtpClient`                                                           | Gibt einen authentifizierten (oder anonymen) XRPC-Client zurück            |
| `public(): BskyClient`                                                                           | Gibt einen Client für authentifizierungsfreie öffentliche Endpunkte zurück |
| `send(BackedEnum\|string $api, string $method, bool $auth, ?array $params, ?callable $callback)` | Ruft eine beliebige AT-Protocol-API direkt auf                             |

### Utilities

| Methode                           | Beschreibung                                               |
| --------------------------------- | ---------------------------------------------------------- |
| `identity(): Identity`            | Gibt den Identity-Service (z. B. für DID-Auflösung) zurück |
| `pds(): PDS`                      | Gibt den PDS-Informationsdienst zurück                     |
| `entryway(?string $path): string` | Gibt die Service-URL (z. B. `bsky.social`) zurück          |
| `publicEndpoint(): string`        | Gibt die URL des öffentlichen Endpunkts zurück             |

## Trait HasShortHand

Der Trait `HasShortHand` verpackt Low-Level-APIs des AT Protocols in verständliche, PHP-freundliche Methoden. Es genügt, im `BlueskyManager` `use HasShortHand;` einzufügen, dann sind diese Methoden direkt über `Bluesky::` aufrufbar.

```php theme={null}
// BlueskyManager.php
class BlueskyManager implements Factory
{
    use Conditionable;
    use HasShortHand;
    use Macroable;
    // ...
}
```

<Info>
  Der Grund, `HasShortHand` als eigenständigen Trait zu führen, liegt in der einfachen Testbarkeit und Anpassbarkeit. So bleibt der Code des `BlueskyManager` schlank, während zahlreiche API-Shortcuts angeboten werden.
</Info>

### Posts und Feed

| Methode                                                         | Beschreibung                         |
| --------------------------------------------------------------- | ------------------------------------ |
| `post(Post\|string\|array $text)`                               | Erstellt einen neuen Post            |
| `getPost(string $uri)`                                          | Ruft einen Post anhand der AT-URI ab |
| `getPosts(array $uris)`                                         | Ruft mehrere Posts auf einmal ab     |
| `deletePost(string $uri)`                                       | Löscht einen Post                    |
| `getTimeline(?string $algorithm, ?int $limit, ?string $cursor)` | Ruft die Home-Timeline ab            |
| `getAuthorFeed(?string $actor, ...)`                            | Ruft den Feed eines Accounts ab      |
| `searchPosts(string $q, ...)`                                   | Durchsucht Posts                     |

### Engagement

| Methode                              | Beschreibung                     |
| ------------------------------------ | -------------------------------- |
| `like(Like\|StrongRef $subject)`     | Erstellt einen Like              |
| `deleteLike(string $uri)`            | Entfernt einen Like              |
| `repost(Repost\|StrongRef $subject)` | Erstellt einen Repost            |
| `deleteRepost(string $uri)`          | Entfernt einen Repost            |
| `getActorLikes(?string $actor, ...)` | Ruft die Likes eines Accounts ab |

### Folgen

| Methode                             | Beschreibung               |
| ----------------------------------- | -------------------------- |
| `follow(Follow\|string $did)`       | Folgen                     |
| `deleteFollow(string $uri)`         | Entfolgen                  |
| `getFollowers(?string $actor, ...)` | Ruft die Follower-Liste ab |
| `getFollows(?string $actor, ...)`   | Ruft die Follows-Liste ab  |

### Profil und Account

| Methode                             | Beschreibung                    |
| ----------------------------------- | ------------------------------- |
| `getProfile(?string $actor)`        | Ruft ein Profil ab              |
| `upsertProfile(callable $callback)` | Aktualisiert das Profil         |
| `resolveHandle(string $handle)`     | Löst ein Handle in eine DID auf |

### Medien

| Methode                                                    | Beschreibung                             |
| ---------------------------------------------------------- | ---------------------------------------- |
| `uploadBlob(StreamInterface\|string $data, string $type)`  | Lädt einen Blob (z. B. ein Bild) hoch    |
| `uploadVideo(StreamInterface\|string $data, string $type)` | Lädt ein Video hoch                      |
| `getJobStatus(string $jobId)`                              | Prüft den Job-Status eines Video-Uploads |
| `getUploadLimits()`                                        | Ruft Upload-Limits ab                    |

### Benachrichtigungen

| Methode                                   | Beschreibung                                      |
| ----------------------------------------- | ------------------------------------------------- |
| `listNotifications(...)`                  | Ruft die Liste der Benachrichtigungen ab          |
| `countUnreadNotifications(...)`           | Ruft die Anzahl ungelesener Benachrichtigungen ab |
| `updateSeenNotifications(string $seenAt)` | Aktualisiert den Gelesen-Status                   |

### AT-Protocol-Record-Operationen

| Methode                                                             | Beschreibung                            |
| ------------------------------------------------------------------- | --------------------------------------- |
| `createRecord(string $repo, string $collection, ...)`               | Erstellt einen Record                   |
| `getRecord(string $repo, string $collection, string $rkey, ...)`    | Ruft einen Record ab                    |
| `listRecords(string $repo, string $collection, ...)`                | Listet Records auf                      |
| `putRecord(string $repo, string $collection, string $rkey, ...)`    | Aktualisiert oder erstellt einen Record |
| `deleteRecord(string $repo, string $collection, string $rkey, ...)` | Löscht einen Record                     |

### Feed-Generator und Labeler

| Methode                                                                | Beschreibung                        |
| ---------------------------------------------------------------------- | ----------------------------------- |
| `publishFeedGenerator(BackedEnum\|string $name, Generator $generator)` | Veröffentlicht einen Feed-Generator |
| `createThreadGate(string $post, ?array $allow)`                        | Erstellt ein Thread-Gate            |
| `upsertLabelDefinitions(callable $callback)`                           | Aktualisiert Label-Definitionen     |
| `deleteLabelDefinitions()`                                             | Löscht Label-Definitionen           |
| `createLabels(RepoRef\|StrongRef\|array $subject, array $labels)`      | Vergibt Labels                      |
| `deleteLabels(RepoRef\|StrongRef\|array $subject, array $labels)`      | Entfernt Labels                     |

## Beispiele für häufig verwendete Shortcuts

### Post erstellen

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;

$response = Bluesky::login(
    identifier: config('bluesky.identifier'),
    password: config('bluesky.password'),
)->post('Hello Bluesky');
```

### Antworten

Für eine Antwort setzen Sie in `Post::build()` den `StrongRef` des übergeordneten Posts. `HasShortHand` besitzt keine spezielle `reply()`-Methode – übergeben Sie `post()` stattdessen ein `Post`-Objekt.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\Types\StrongRef;

$parent = StrongRef::to(uri: 'at://did:plc:.../app.bsky.feed.post/...', cid: 'bafyrei...');

$reply = Post::create('Text der Antwort')
    ->reply(root: $parent, parent: $parent);

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->post($reply);
```

### Likes und Reposts

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Types\StrongRef;

$ref = StrongRef::to(uri: 'at://did:plc:.../app.bsky.feed.post/...', cid: 'bafyrei...');

// Like
Bluesky::withToken($session)->like($ref);

// Repost
Bluesky::withToken($session)->repost($ref);
```

### Profil aktualisieren

`upsertProfile()` lädt das aktuelle Profil und speichert die in der Closure vorgenommenen Änderungen. Über die Methoden des `Profile`-Objekts lassen sich unter anderem `displayName` und `description` setzen.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Profile;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->upsertProfile(function (Profile $profile) {
        $profile->displayName('Neuer Anzeigename')
                ->description('Profilbeschreibung');
    });
```

#### Self-Labels setzen

Mit `SelfLabels` können Sie Self-Labels für den gesamten Account setzen.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Profile;
use Revolution\Bluesky\Types\SelfLabels;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->upsertProfile(function (Profile $profile) {
        $profile->labels(SelfLabels::make(['!no-unauthenticated']));
    });
```

### Beliebige API direkt aufrufen

Für Methoden, die nicht in `HasShortHand` enthalten sind, verwenden Sie `send()` oder `client()`.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;

// Eine beliebige XRPC-Methode über send() aufrufen
$response = Bluesky::withToken($session)
    ->send(
        api: 'app.bsky.actor.getProfiles',
        method: 'get',
        params: ['actors' => ['did:plc:...', 'did:plc:...']],
    );

// Feinere Steuerung über client()
$response = Bluesky::withToken($session)
    ->client()
    ->bsky()
    ->getProfiles(actors: ['did:plc:...']);
```

## Unterschied zwischen Nutzung über die Facade und direkter Nutzung

`Bluesky::post()` und `app(Factory::class)->post()` sind Operationen auf derselben `BlueskyManager`-Instanz.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Contracts\Factory;

// Über die Facade (übliche Verwendung)
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->post('Hello');

// Direkt aus dem Container abrufen
$manager = app(Factory::class);
$manager->login(config('bluesky.identifier'), config('bluesky.password'))
        ->post('Hello');

// Per DI über Type-Hint
class MyService
{
    public function __construct(private Factory $bluesky) {}

    public function doPost(): void
    {
        $this->bluesky->login(
            config('bluesky.identifier'),
            config('bluesky.password'),
        )->post('Hello from DI');
    }
}
```

Durch die `scoped`-Registrierung bleibt der nach `login()` bestehende Session-Zustand innerhalb eines Requests erhalten.

## Conditionable und Macroable

`BlueskyManager` verwendet zusätzlich die Traits `Conditionable` und `Macroable`.

### Conditionable: when() / unless()

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->when(config('app.env') === 'production', function ($bluesky) {
        $bluesky->post('Post aus der Produktionsumgebung');
    });
```

### Macroable: Eigene Methoden hinzufügen

Mit `macro()` können Sie aus der Anwendung Methoden zum `BlueskyManager` hinzufügen. Üblicherweise definiert man sie in der `boot()`-Methode des `AppServiceProvider`.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;

Bluesky::macro('postWithHashtag', function (string $text, string $tag) {
    /** @var \Revolution\Bluesky\BlueskyManager $this */
    return $this->post("{$text} #{$tag}");
});

// Anwendungsbeispiel
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->postWithHashtag('Hello', 'laravel');
```

## Eigenen Agent einsetzen

Über `withAgent()` können Sie eine beliebige `Agent`-Implementierung direkt einsetzen.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Contracts\Agent;

// Ein eigener Agent muss das Agent-Interface implementieren
class MyCustomAgent implements Agent
{
    // ...
}

Bluesky::withAgent(new MyCustomAgent());
```

<Info>
  Da bei der üblichen Authentifizierung (`login()` / `withToken()`) der Agent automatisch gesetzt wird, ist `withAgent()` vor allem beim Testen relevant.
</Info>

## Weiterführende Links

* [Vergleich der Authentifizierungsmethoden](/de/packages/laravel-bluesky/authentication) – Details zu App Password und OAuth
* [Basic client](/de/packages/laravel-bluesky/basic-client) – API-Beispiele nach der Authentifizierung
* [Tests](/de/packages/laravel-bluesky/testing) – Testmethoden mit Fake
* Source: [src/BlueskyManager.php](https://github.com/invokable/laravel-bluesky/blob/main/src/BlueskyManager.php)
* Source: [src/HasShortHand.php](https://github.com/invokable/laravel-bluesky/blob/main/src/HasShortHand.php)


## Related topics

- [Bot-Tutorial - Laravel Bluesky](/de/packages/laravel-bluesky/bot-tutorial.md)
- [Vergleich der Authentifizierungsmethoden - Laravel Bluesky](/de/packages/laravel-bluesky/authentication.md)
- [Socialite - Laravel Bluesky](/de/packages/laravel-bluesky/socialite.md)
- [Illuminate\Support\Manager — Anatomie des Treibersystems](/de/advanced/manager.md)
- [Laravel Bluesky](/de/packages/laravel-bluesky/index.md)
