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

# 알림 채널 - Laravel Bluesky

> Laravel Notification 채널로서 Bluesky에 게시·DM을 전송하는 방법을 설명합니다.

## 개요

`laravel-bluesky`는 Laravel의 Notification 시스템에 통합할 수 있습니다. `BlueskyChannel`로 일반 게시, `BlueskyPrivateChannel`로 다이렉트 메시지(DM)를 보낼 수 있습니다.

```mermaid theme={null}
flowchart LR
    A["Laravel Notification"] --> B{"채널 선택"}
    B -->|"BlueskyChannel"| C["Bluesky 일반 게시"]
    B -->|"BlueskyPrivateChannel"| D["Bluesky DM 전송"]
```

## 이용 가능한 채널

| 채널                      | 용도                      |
| ----------------------- | ----------------------- |
| `BlueskyChannel`        | 일반 공개 게시로서 알리기          |
| `BlueskyPrivateChannel` | 수신자에게 DM(프라이빗 채팅)으로 알리기 |

## Notification 클래스

### BlueskyChannel

`via()`에서 `BlueskyChannel::class`를 지정하고, `toBluesky()`에서 전송 내용을 반환합니다.

```php theme={null}
use Illuminate\Notifications\Notification;
use Revolution\Bluesky\Notifications\BlueskyChannel;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;
use Revolution\Bluesky\Embed\External;

class TestNotification extends Notification
{
    public function via(object $notifiable): array
    {
        return [
            BlueskyChannel::class
        ];
    }

    public function toBluesky(object $notifiable): Post
    {
        $external = External::create(title: 'Title', description: 'test', uri: 'https://');

        return Post::build(function (TextBuilder $builder) {
                   $builder->text('test')
                           ->newLine()
                           ->tag('#Laravel');
        })->embed($external);
    }
}
```

`Post`의 사용법은 [Basic client](/ko/packages/laravel-bluesky/basic-client)와 동일합니다. TextBuilder, Embed 등도 동일하게 이용할 수 있습니다.

### BlueskyPrivateChannel

`BlueskyPrivateMessage`는 `Post`와 거의 동일하지만, text·facets·embed만 지원합니다. embed는 `QuoteRecord`만 지원합니다.

```php theme={null}
use Illuminate\Notifications\Notification;
use Revolution\Bluesky\Notifications\BlueskyPrivateChannel;
use Revolution\Bluesky\Notifications\BlueskyPrivateMessage;
use Revolution\Bluesky\RichText\TextBuilder;
use Revolution\Bluesky\Embed\QuoteRecord;
use Revolution\Bluesky\Types\StrongRef;

class TestNotification extends Notification
{
    public function via(object $notifiable): array
    {
        return [
            BlueskyPrivateChannel::class
        ];
    }

    public function toBlueskyPrivate(object $notifiable): BlueskyPrivateMessage
    {
        $quote = QuoteRecord::create(StrongRef::to(uri: 'at://', cid: 'cid'));

        return BlueskyPrivateMessage::build(function (TextBuilder $builder) {
                   $builder->text('test')
                           ->newLine()
                           ->tag('#Laravel');
        })->embed($quote);
    }
}
```

## 온디맨드 알림

모델을 사용하지 않고 그 자리에서 알림 대상을 지정하는 경우에는 `Notification::route()`를 사용합니다.

### BlueskyChannel

```php theme={null}
use Illuminate\Support\Facades\Notification;
use Revolution\Bluesky\Notifications\BlueskyRoute;
use Revolution\Bluesky\Session\OAuthSession;
use App\Models\User;

// App password
Notification::route('bluesky', BlueskyRoute::to(identifier: config('bluesky.identifier'), password: config('bluesky.password')))
            ->notify(new TestNotification());

// OAuth
$user = User::find(1);
$session = OAuthSession::create([
    'did' => $user->did,
    'iss' => $user->iss,
    'refresh_token' => $user->refresh_token,
]);
Notification::route('bluesky', BlueskyRoute::to(oauth: $session))
            ->notify(new TestNotification());
```

### BlueskyPrivateChannel

DM에는 `receiver`(전송 대상의 DID 또는 핸들) 지정이 필수입니다. 또한 수신자 측에서 DM 수신이 활성화되어 있어야 합니다.

* App password의 경우에는 DM 전송 권한이 필요합니다.
* OAuth의 경우에는 `transition:chat.bsky` 스코프가 필요합니다.

```php theme={null}
use Illuminate\Support\Facades\Notification;
use Revolution\Bluesky\Notifications\BlueskyRoute;
use Revolution\Bluesky\Session\OAuthSession;
use App\Models\User;

// App password
Notification::route('bluesky-private', BlueskyRoute::to(identifier: config('bluesky.identifier'), password: config('bluesky.password'), receiver: 'did or handle'))
            ->notify(new TestNotification());

// OAuth
$user = User::find(1);
$session = OAuthSession::create([
    'did' => $user->did,
    'iss' => $user->iss,
    'refresh_token' => $user->refresh_token,
]);
Notification::route('bluesky-private', BlueskyRoute::to(oauth: $session, receiver: 'did or handle'))
            ->notify(new TestNotification());
```

<Info>
  Bluesky에서는 자기 자신에게 DM을 보낼 수 없습니다. 본인 앞으로 알리고 싶은 경우에는 전송용으로 별도 계정을 준비하세요.
</Info>

```php theme={null}
// 자신에게 DM을 보내는 경우

use Illuminate\Support\Facades\Notification;
use Revolution\Bluesky\Notifications\BlueskyRoute;

Notification::route('bluesky-private', BlueskyRoute::to(identifier: 'sender identifier', password: 'sender password', receiver: 'your did or handle'))
            ->notify(new TestNotification());
```

개인 용도 등, 항상 동일한 발신자·수신자를 사용하는 경우에는 `.env`로 설정할 수 있습니다. 전송 전용 계정을 만들어 주세요.

```dotenv theme={null}
BLUESKY_SENDER_IDENTIFIER=sender did or handle
BLUESKY_SENDER_APP_PASSWORD=sender password
BLUESKY_RECEIVER=your did or handle
```

```php theme={null}
Notification::route('bluesky-private', BlueskyRoute::to(
    identifier: config('bluesky.notification.private.sender.identifier'),
    password: config('bluesky.notification.private.sender.password'),
    receiver: config('bluesky.notification.private.receiver'),
))->notify(new TestNotification());
```

## 사용자 알림

`Notifiable` 트레이트를 사용하는 모델에 알림 라우팅을 정의합니다.

### BlueskyChannel

```php theme={null}
use Illuminate\Notifications\Notifiable;
use Revolution\Bluesky\Notifications\BlueskyRoute;
use Revolution\Bluesky\Session\OAuthSession;

class User
{
    use Notifiable;

    public function routeNotificationForBluesky($notification): BlueskyRoute
    {
        // App password
        return BlueskyRoute::to(identifier: $this->bluesky_identifier, password: $this->bluesky_password);

        // OAuth
        $session = OAuthSession::create([
            'did' => $this->did,
            'iss' => $this->iss,
            'refresh_token' => $this->refresh_token,
        ]);
        return BlueskyRoute::to(oauth: $session);
    }
}

$user->notify(new TestNotification());
```

### BlueskyPrivateChannel

```php theme={null}
use Illuminate\Notifications\Notifiable;
use Revolution\Bluesky\Notifications\BlueskyRoute;
use Revolution\Bluesky\Session\OAuthSession;

class User
{
    use Notifiable;

    public function routeNotificationForBlueskyPrivate($notification): BlueskyRoute
    {
        // App password
        return BlueskyRoute::to(identifier: $this->bluesky_identifier, password: $this->bluesky_password, receiver: $this->receiver);

        // OAuth
        $session = OAuthSession::create([
            'did' => $this->did,
            'iss' => $this->iss,
            'refresh_token' => $this->refresh_token,
        ]);
        return BlueskyRoute::to(oauth: $session, receiver: $this->receiver);
    }
}

$user->notify(new TestNotification());
```

사용자 자신을 수신자로 설정할 수도 있습니다.

```php theme={null}
public function routeNotificationForBlueskyPrivate($notification): BlueskyRoute
{
    // App password
    return BlueskyRoute::to(identifier: 'sender identifier', password: 'sender password', receiver: $this->did);
}
```

## BlueskyRoute

인증 방식(App password 또는 OAuth)에 따라 지정 방법이 다릅니다. 이름 있는 인수 사용을 권장합니다.

```php theme={null}
use Revolution\Bluesky\Notifications\BlueskyRoute;
use Revolution\Bluesky\Session\OAuthSession;

// App password
BlueskyRoute::to(identifier: config('bluesky.identifier'), password: config('bluesky.password'))

// OAuth
$session = OAuthSession::create([
    'did' => '...',
    'iss' => '...',
    'refresh_token' => '...',
]);
BlueskyRoute::to(oauth: $session);
```

<Tip>
  자신의 계정으로만 알린다면 **App password**가 가장 단순합니다. refresh\_token 갱신을 고려할 필요가 없고, `.env`를 설정하기만 하면 사용할 수 있습니다.

  ```dotenv theme={null}
  BLUESKY_IDENTIFIER=
  BLUESKY_APP_PASSWORD=
  ```
</Tip>

## 알림 결과 확인

일반적인 Laravel과 마찬가지로 `NotificationSent` 이벤트에서 알림 후의 응답을 확인할 수 있습니다.

```php theme={null}
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Http\Client\Response;

class Listener
{
    public function handle(NotificationSent $event): void
    {
        // $event->channel  BlueskyChannel
        // $event->notifiable
        // $event->notification
        // $event->response  null|Response
    }
}
```

<Info>
  Source: [docs/notification.md](https://github.com/invokable/laravel-bluesky/blob/main/docs/notification.md)
</Info>


## Related topics

- [알림 채널 - LINE SDK for Laravel](/ko/packages/laravel-line-sdk/notification.md)
- [알림(Notifications)](/ko/notifications.md)
- [Laravel Bluesky](/ko/packages/laravel-bluesky/index.md)
- [인증 방식 비교 - Laravel Bluesky](/ko/packages/laravel-bluesky/authentication.md)
- [봇 튜토리얼 - Laravel Bluesky](/ko/packages/laravel-bluesky/bot-tutorial.md)
