> ## 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 y HasShortHand

> Explica la arquitectura de BlueskyManager, la implementación real detrás del Facade Bluesky, y los métodos de atajo que aporta el trait HasShortHand.

## Descripción general

El Facade `Bluesky` es una capa fina sobre `BlueskyManager`. `BlueskyManager` gestiona varios Agents, y el trait `HasShortHand` agrupa los atajos de los métodos que se usan con más frecuencia.

## Arquitectura

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

`BlueskyServiceProvider` registra `Factory::class` como `BlueskyManager` con un singleton scoped (una instancia por petición).

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

El `getFacadeAccessor()` del Facade resuelve este binding.

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

## Métodos principales de BlueskyManager

A continuación se listan los métodos principales que define el propio `BlueskyManager`. Distinguirlos de los métodos del trait `HasShortHand` ayuda a entender la estructura interna.

### Autenticación

| Método                                                                 | Descripción                                                             |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `login(string $identifier, string $password, ?string $service = null)` | Autentica con App Password y configura un `LegacyAgent`                 |
| `withToken(?AbstractSession $token)`                                   | Recibe un `OAuthSession` o `LegacySession` y configura el Agent         |
| `check(): bool`                                                        | Comprueba si hay autenticación (también verifica la vigencia del token) |
| `refreshSession()`                                                     | Refresca los tokens                                                     |
| `logout()`                                                             | Limpia el Agent                                                         |

### Operaciones del Agent

| Método                     | Descripción                                                             |
| -------------------------- | ----------------------------------------------------------------------- |
| `agent(): ?Agent`          | Obtiene el Agent actual                                                 |
| `withAgent(?Agent $agent)` | Configura el Agent directamente                                         |
| `assertDid(): string`      | Devuelve el DID autenticado. Lanza una excepción si no está autenticado |

### Cliente HTTP

| Método                                                                                           | Descripción                                                 |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| `client(bool $auth = true): AtpClient`                                                           | Devuelve un cliente XRPC autenticado (o anónimo)            |
| `public(): BskyClient`                                                                           | Devuelve un cliente de endpoints públicos sin autenticación |
| `send(BackedEnum\|string $api, string $method, bool $auth, ?array $params, ?callable $callback)` | Invoca directamente cualquier API del AT Protocol           |

### Utilidades

| Método                            | Descripción                                               |
| --------------------------------- | --------------------------------------------------------- |
| `identity(): Identity`            | Obtiene el servicio de identidad para resolver DIDs, etc. |
| `pds(): PDS`                      | Obtiene el servicio de información de PDS                 |
| `entryway(?string $path): string` | Devuelve la URL del servicio (por ejemplo `bsky.social`)  |
| `publicEndpoint(): string`        | Devuelve la URL del endpoint público                      |

## Trait HasShortHand

El trait `HasShortHand` envuelve las APIs de bajo nivel del AT Protocol en métodos claros y orientados a PHP. Basta con añadir `use HasShortHand;` en `BlueskyManager` para que estos métodos puedan invocarse directamente desde `Bluesky::`.

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

<Info>
  `HasShortHand` se mantiene como un trait independiente para facilitar las pruebas y la personalización. Gracias a este trait, el código de `BlueskyManager` sigue siendo simple mientras se ofrece un amplio conjunto de atajos de API.
</Info>

### Publicaciones y feed

| Método                                                          | Descripción                                   |
| --------------------------------------------------------------- | --------------------------------------------- |
| `post(Post\|string\|array $text)`                               | Crea una nueva publicación                    |
| `getPost(string $uri)`                                          | Obtiene una publicación a partir de su AT-URI |
| `getPosts(array $uris)`                                         | Obtiene varias publicaciones a la vez         |
| `deletePost(string $uri)`                                       | Elimina una publicación                       |
| `getTimeline(?string $algorithm, ?int $limit, ?string $cursor)` | Obtiene la timeline de inicio                 |
| `getAuthorFeed(?string $actor, ...)`                            | Obtiene el feed de la cuenta indicada         |
| `searchPosts(string $q, ...)`                                   | Busca publicaciones                           |

### Interacciones

| Método                               | Descripción                            |
| ------------------------------------ | -------------------------------------- |
| `like(Like\|StrongRef $subject)`     | Crea un like                           |
| `deleteLike(string $uri)`            | Elimina un like                        |
| `repost(Repost\|StrongRef $subject)` | Crea un repost                         |
| `deleteRepost(string $uri)`          | Elimina un repost                      |
| `getActorLikes(?string $actor, ...)` | Obtiene la lista de likes de la cuenta |

### Follow

| Método                              | Descripción                   |
| ----------------------------------- | ----------------------------- |
| `follow(Follow\|string $did)`       | Follow                        |
| `deleteFollow(string $uri)`         | Deshace el follow             |
| `getFollowers(?string $actor, ...)` | Obtiene la lista de followers |
| `getFollows(?string $actor, ...)`   | Obtiene la lista de following |

### Perfil y cuenta

| Método                              | Descripción              |
| ----------------------------------- | ------------------------ |
| `getProfile(?string $actor)`        | Obtiene el perfil        |
| `upsertProfile(callable $callback)` | Actualiza el perfil      |
| `resolveHandle(string $handle)`     | Resuelve un handle a DID |

### Multimedia

| Método                                                     | Descripción                                    |
| ---------------------------------------------------------- | ---------------------------------------------- |
| `uploadBlob(StreamInterface\|string $data, string $type)`  | Sube blobs como imágenes                       |
| `uploadVideo(StreamInterface\|string $data, string $type)` | Sube un vídeo                                  |
| `getJobStatus(string $jobId)`                              | Comprueba el estado del job de subida de vídeo |
| `getUploadLimits()`                                        | Obtiene los límites de subida                  |

### Notificaciones

| Método                                    | Descripción                                  |
| ----------------------------------------- | -------------------------------------------- |
| `listNotifications(...)`                  | Obtiene la lista de notificaciones           |
| `countUnreadNotifications(...)`           | Obtiene el número de notificaciones sin leer |
| `updateSeenNotifications(string $seenAt)` | Actualiza el estado leído                    |

### Operaciones sobre records del AT Protocol

| Método                                                              | Descripción                 |
| ------------------------------------------------------------------- | --------------------------- |
| `createRecord(string $repo, string $collection, ...)`               | Crea un record              |
| `getRecord(string $repo, string $collection, string $rkey, ...)`    | Obtiene un record           |
| `listRecords(string $repo, string $collection, ...)`                | Obtiene la lista de records |
| `putRecord(string $repo, string $collection, string $rkey, ...)`    | Actualiza o crea un record  |
| `deleteRecord(string $repo, string $collection, string $rkey, ...)` | Elimina un record           |

### Feed generator y labeler

| Método                                                                 | Descripción                             |
| ---------------------------------------------------------------------- | --------------------------------------- |
| `publishFeedGenerator(BackedEnum\|string $name, Generator $generator)` | Publica un feed generator               |
| `createThreadGate(string $post, ?array $allow)`                        | Crea un thread gate                     |
| `upsertLabelDefinitions(callable $callback)`                           | Actualiza las definiciones de etiquetas |
| `deleteLabelDefinitions()`                                             | Elimina las definiciones de etiquetas   |
| `createLabels(RepoRef\|StrongRef\|array $subject, array $labels)`      | Aplica etiquetas                        |
| `deleteLabels(RepoRef\|StrongRef\|array $subject, array $labels)`      | Elimina etiquetas                       |

## Ejemplos de atajos habituales

### Publicar

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

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

### Responder

Para responder, configura el `StrongRef` de la publicación padre con `Post::build()`. `HasShortHand` no dispone de un método `reply()` específico; se pasa el objeto `Post` a `post()`.

```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('texto de la respuesta')
    ->reply(root: $parent, parent: $parent);

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

### Like y repost

```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);
```

### Actualizar el perfil

`upsertProfile()` obtiene el perfil actual y guarda los cambios que se realizan dentro de la clausura. Puedes usar los métodos del objeto `Profile` para configurar displayName, description, etc.

```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('Nuevo nombre mostrado')
                ->description('Texto descriptivo del perfil');
    });
```

#### Configurar self labels

Con `SelfLabels` puedes aplicar self labels a toda la cuenta.

```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']));
    });
```

### Llamar directamente a una API arbitraria

Para métodos que no están en `HasShortHand`, usa `send()` o `client()`.

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

// Invoca un método XRPC arbitrario con send()
$response = Bluesky::withToken($session)
    ->send(
        api: 'app.bsky.actor.getProfiles',
        method: 'get',
        params: ['actors' => ['did:plc:...', 'did:plc:...']],
    );

// Con client() puedes hacer operaciones más finas
$response = Bluesky::withToken($session)
    ->client()
    ->bsky()
    ->getProfiles(actors: ['did:plc:...']);
```

## Diferencias entre usar el Facade y usarlo directamente

`Bluesky::post()` y `app(Factory::class)->post()` son operaciones sobre la misma instancia de `BlueskyManager`.

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

// A través del Facade (uso habitual)
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->post('Hello');

// Obtener directamente del contenedor
$manager = app(Factory::class);
$manager->login(config('bluesky.identifier'), config('bluesky.password'))
        ->post('Hello');

// Inyección por tipo
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');
    }
}
```

Como el registro es `scoped`, dentro de una misma petición se conserva el estado de la sesión tras `login()`.

## Conditionable y Macroable

`BlueskyManager` también usa los traits `Conditionable` y `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('Publicación desde el entorno de producción');
    });
```

### Macroable: añadir métodos personalizados

Con `macro()` puedes añadir métodos a `BlueskyManager` desde la aplicación. Es habitual definirlos dentro de `boot()` en `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}");
});

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

## Inyectar un Agent personalizado

Con `withAgent()` puedes asignar directamente cualquier implementación de `Agent`.

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

// Si implementas un Agent personalizado, implementa la interfaz Agent
class MyCustomAgent implements Agent
{
    // ...
}

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

<Info>
  Con la autenticación habitual (`login()` / `withToken()`) el agente se configura automáticamente, por lo que `withAgent()` suele usarse sobre todo en pruebas.
</Info>

## Enlaces de referencia

* [Comparativa de métodos de autenticación](/es/packages/laravel-bluesky/authentication) — detalles de App Password y OAuth
* [Basic client](/es/packages/laravel-bluesky/basic-client) — ejemplos de operaciones de API tras autenticarse
* [Testing](/es/packages/laravel-bluesky/testing) — métodos de prueba usando 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

- [Tutorial de bots - Laravel Bluesky](/es/packages/laravel-bluesky/bot-tutorial.md)
- [Comparativa de métodos de autenticación - Laravel Bluesky](/es/packages/laravel-bluesky/authentication.md)
- [Socialite - Laravel Bluesky](/es/packages/laravel-bluesky/socialite.md)
- [Identity](/es/packages/laravel-bluesky/identity.md)
- [TextBuilder](/es/packages/laravel-bluesky/text-builder.md)
