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

# WebSocket (Jetstream / Firehose)

> Laravel Bluesky 的 WebSocket 功能。透過 Bluesky Jetstream 與 AT Protocol Firehose 進行即時事件處理。介紹 JetstreamServeCommand 與 FirehoseServeCommand 的使用方式。

## 概觀

`laravel-bluesky` 提供兩個 WebSocket 指令，可連接至 Bluesky 的即時串流。

* **Jetstream** — Bluesky 專屬的已過濾 WebSocket 端點。以 JSON 格式提供，輕量。
* **Firehose** — AT Protocol 的原始事件串流。以 DAG-CBOR 二進位格式接收所有資料。

```mermaid theme={null}
graph LR
    subgraph "Bluesky 網路"
        J["Jetstream<br>jetstream1.us-west.bsky.network<br>JSON / 可過濾"]
        F["Firehose<br>bsky.network<br>DAG-CBOR / 全部資料"]
    end
    subgraph "Laravel 應用程式"
        WS["JetstreamServeCommand<br>FirehoseServeCommand<br>(Workerman)"]
        E["Laravel Events<br>JetstreamCommitMessage<br>FirehoseCommitMessage 等"]
        L["Listener / Job"]
    end

    J -->|WebSocket| WS
    F -->|WebSocket| WS
    WS --> E --> L
```

<Warning>
  WebSocket 這類長時間執行的程序需要 **VPS 或 EC2 等常態啟動的伺服器**，或 **Laravel Cloud 的自訂 worker**。無法在 Laravel Vapor 或 Vercel 等無伺服器環境運行。
</Warning>

## 安裝

WebSocket 功能需要 [Workerman](https://github.com/walkor/workerman)。

```bash theme={null}
composer require workerman/workerman
```

## Jetstream

### 概觀

Jetstream 是 Bluesky 提供的已過濾 WebSocket 服務。可依 collection 種別或使用者 DID 過濾，因此能高效地只接收所需事件。

| 特性   | 說明                   |
| ---- | -------------------- |
| 資料格式 | JSON                 |
| 過濾   | 可依 collection、DID 過濾 |
| 資料量  | 視過濾條件而定，較輕量          |
| 用途   | 貼文、按讚、追蹤等監控          |

### 啟動方式

```bash theme={null}
# 接收所有訊息（不過濾）
php artisan bluesky:ws start

# 除錯：顯示所有接收到的訊息
php artisan bluesky:ws start -v
```

### Collection 過濾

透過 `-C` 選項篩選要接收的 collection。可指定多個。

```bash theme={null}
# 僅接收貼文與按讚
php artisan bluesky:ws start -C app.bsky.feed.post -C app.bsky.feed.like

# 僅接收追蹤
php artisan bluesky:ws start -C app.bsky.graph.follow
```

主要 collection：

| Collection              | 內容       |
| ----------------------- | -------- |
| `app.bsky.feed.post`    | 貼文的建立、刪除 |
| `app.bsky.feed.like`    | 按讚       |
| `app.bsky.feed.repost`  | 轉發       |
| `app.bsky.graph.follow` | 追蹤       |
| `app.bsky.graph.block`  | 封鎖       |

### DID 過濾

透過 `-D` 選項可只接收特定使用者的事件。

```bash theme={null}
# 僅接收特定使用者的貼文
php artisan bluesky:ws start -C app.bsky.feed.post -D did:plc:xxx -D did:plc:yyy
```

### 事件處理

Jetstream 指令會依接收訊息的種別觸發 Laravel 事件。

| 事件類別                       | 觸發時機                    |
| -------------------------- | ----------------------- |
| `JetstreamMessageReceived` | 收到所有訊息時                 |
| `JetstreamCommitMessage`   | record 建立、更新、刪除時        |
| `JetstreamIdentityMessage` | handle 變更等 identity 事件時 |
| `JetstreamAccountMessage`  | 帳號啟用、停用時                |

建立事件監聽器處理事件。

```bash theme={null}
php artisan make:listener JetstreamPostListener
```

```php theme={null}
namespace App\Listeners;

use Revolution\Bluesky\Events\Jetstream\JetstreamCommitMessage;

class JetstreamPostListener
{
    public function handle(JetstreamCommitMessage $event): void
    {
        // 確認 collection 種別
        $collection = data_get($event->message, 'commit.collection');

        if ($collection !== 'app.bsky.feed.post') {
            return;
        }

        // 操作類別：create / update / delete
        $operation = $event->operation;

        // 貼文者的 DID
        $did = $event->message['did'];

        // record 內容
        $record = data_get($event->message, 'commit.record');
        $text = data_get($record, 'text', '');

        info("[$operation] $did: $text");
    }
}
```

## Firehose

### 概觀

Firehose 是 AT Protocol 的原始事件串流。可以二進位（DAG-CBOR）格式接收 Bluesky 網路上所有 record 操作。

| 特性   | 說明                    |
| ---- | --------------------- |
| 資料格式 | DAG-CBOR 二進位（套件會自動解碼） |
| 過濾   | 無（接收全部資料）             |
| 資料量  | 非常龐大                  |
| 用途   | 全資料的蒐集、封存             |

<Info>
  套件會自動處理 DAG-CBOR 解碼。事件監聽器中可以像一般 PHP 陣列一樣接收資料。
</Info>

### 啟動方式

```bash theme={null}
php artisan bluesky:firehose start

# 除錯：顯示接收到的訊息
php artisan bluesky:firehose start -v
```

### 事件處理

Firehose 指令也透過 Laravel 事件處理訊息。

| 事件類別                      | 觸發時機             |
| ------------------------- | ---------------- |
| `FirehoseMessageReceived` | 收到所有訊息時（含原始資料）   |
| `FirehoseCommitMessage`   | record 建立、更新、刪除時 |
| `FirehoseIdentityMessage` | identity 事件時     |
| `FirehoseAccountMessage`  | 帳號事件時            |
| `FirehoseSyncMessage`     | 儲存庫同步事件時         |

```bash theme={null}
php artisan make:listener FirehosePostListener
```

```php theme={null}
namespace App\Listeners;

use Revolution\Bluesky\Events\Firehose\FirehoseCommitMessage;

class FirehosePostListener
{
    public function handle(FirehoseCommitMessage $event): void
    {
        // 確認 collection 種別
        if ($event->collection !== 'app.bsky.feed.post') {
            return;
        }

        // 操作類別：create / update / delete
        $action = $event->action;

        // 貼文者的 DID
        $did = $event->did;

        // record 內容（已解碼的陣列）
        $record = $event->record;
        $text = data_get($record, 'value.text', '');

        info("[$action] $did: $text");
    }
}
```

## 設定

可於 `config/bluesky.php` 變更連線主機或日誌設定。

```php theme={null}
// Jetstream
'jetstream' => [
    'host' => env('BLUESKY_JETSTREAM_HOST', 'jetstream1.us-west.bsky.network'),
    'max' => env('BLUESKY_JETSTREAM_MAX', 0), // maxMessageSizeBytes (0 = 無限制)
    'logging' => [
        'driver' => env('BLUESKY_JETSTREAM_LOG_DRIVER', 'daily'),
        'days' => 7,
        'path' => env('BLUESKY_JETSTREAM_LOG_PATH', storage_path('logs/jetstream.log')),
    ],
],

// Firehose
'firehose' => [
    'host' => env('BLUESKY_FIREHOSE_HOST', 'bsky.network'),
    'logging' => [
        'driver' => env('BLUESKY_FIREHOSE_LOG_DRIVER', 'daily'),
        'days' => 7,
        'path' => env('BLUESKY_FIREHOSE_LOG_PATH', storage_path('logs/firehose.log')),
    ],
],
```

`.env` 設定範例：

```ini theme={null}
BLUESKY_JETSTREAM_HOST=jetstream2.us-east.bsky.network
BLUESKY_JETSTREAM_MAX=1000000
```

## 與 Labeler 組合

可同時啟動 Labeler 伺服器與 Jetstream / Firehose。Labeler 在處理接收到的標籤請求時，可利用 Jetstream 或 Firehose 的資料。

```bash theme={null}
# Labeler + Jetstream（監控追蹤事件）
php artisan bluesky:labeler:server start --jetstream -C app.bsky.graph.follow

# Labeler + Firehose（接收全部資料）
php artisan bluesky:labeler:server start --firehose
```

<Info>
  Labeler 的詳情請參考 [Labeler 頁面](/zh-TW/packages/laravel-bluesky/labeler)。
</Info>

## 長時間執行程序的運作

WebSocket 指令是需要長時間常駐的程序。正式環境中請使用 Supervisor 等程序管理工具。

### Supervisor 設定範例

`/etc/supervisor/conf.d/bluesky-jetstream.conf`：

```ini theme={null}
[program:bluesky-jetstream]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan bluesky:ws start -C app.bsky.feed.post
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/jetstream-worker.log
stopwaitsecs=3600
```

```bash theme={null}
# 重新載入 Supervisor 並啟動
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start bluesky-jetstream:*
```

### Laravel Forge 的 daemon 設定

若使用 Laravel Forge，可從 **Daemons** 區塊新增 daemon。

* **Command**：`php artisan bluesky:ws start -C app.bsky.feed.post`
* **Directory**：`/var/www/html`
* **User**：`forge`

### Laravel Cloud 的背景程序設定

由於 WebSocket 指令是作為 **WebSocket client** 連線至 Bluesky 的串流，因此也可在 Laravel Cloud 執行。請將其設為 Laravel Cloud 的背景程序（自訂 worker）。

在 Laravel Cloud 的背景程序設定中新增 **Custom Worker**。

**Jetstream 時：**

```bash theme={null}
php artisan bluesky:ws start
```

**Firehose 時：**

```bash theme={null}
php artisan bluesky:firehose start
```

<Info>
  部署時的程序停止與重啟等作業，Laravel Cloud 端會自動處理。除背景程序設定外，無需其他額外設定。
</Info>

### 注意事項

* 程序若意外終止，會由 `autorestart=true` 自動重啟。
* 為防止記憶體洩漏，建議考慮定期重啟。
* 在會接收大量訊息的 Firehose 中，建議將 listener 內的處理改為非同步（Queue Job）。

```php theme={null}
// 於 listener 中派送至 Queue Job 的範例

namespace App\Listeners;

use App\Jobs\ProcessFirehosePost;
use Revolution\Bluesky\Events\Firehose\FirehoseCommitMessage;

class FirehosePostListener
{
    public function handle(FirehoseCommitMessage $event): void
    {
        if ($event->collection !== 'app.bsky.feed.post') {
            return;
        }

        // 較重的處理交由 Queue Job
        ProcessFirehosePost::dispatch($event->did, $event->record, $event->action);
    }
}
```

<Info>
  Source：[src/Console/WebSocket](https://github.com/invokable/laravel-bluesky/tree/main/src/Console/WebSocket)
</Info>


## Related topics

- [Laravel Cloud — Laravel 專用 PaaS 全貌](/zh-TW/blog/laravel-cloud.md)
- [Streaming](/zh-TW/packages/laravel-copilot-sdk/streaming.md)
- [Laravel Nostr](/zh-TW/packages/laravel-nostr.md)
- [Laravel Reverb](/zh-TW/reverb.md)
- [Laravel Notification for Discord(Webhook)](/zh-TW/packages/laravel-notification-discord-webhook.md)
