> ## 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 Nostr

> Nostr 協定的 Laravel 套件。Key 管理、事件操作、pool 支援、NIP-05 / NIP-17、Laravel Notifications 整合。

## 概要

[revolution/laravel-nostr](https://github.com/invokable/laravel-nostr) 是從 Laravel 使用 Nostr 協定的套件。提供 Key 的生成與轉換、事件的取得與發佈、pool(多 relay)支援、NIP-05 profile、NIP-17 Private Direct Messages,以及與 Laravel Notifications 的整合。

<Info>
  由於 Nostr 規範仍在演進中,本套件也持續開發中。通知功能已可實用。
</Info>

## Driver

本套件有 2 個 driver。

| Driver   | 說明                                                                                   |
| -------- | ------------------------------------------------------------------------------------ |
| `native` | 純 PHP 的實作。使用 [nostr-php](https://github.com/nostrver-se/nostr-php)。目前使用此 driver 已足夠。 |
| `node`   | 依賴外部 [WebAPI](https://github.com/kawax/nostr-vercel-api)(Node.js)的實作。                |

<Tip>
  `native` driver 的獨特之處在於 `WebSocketHttpMixin` 的實作。使用 Laravel 的 HTTP client 連線至 WebSocket,資料收發後立即中斷連線。不需要持續執行 WebSocket 伺服器,任何 Laravel 使用者都能使用。
</Tip>

<Info>
  `native` driver 不支援 NIP-04。
</Info>

### 預設 driver 設定

於 `config/nostr.php` 或 `.env` 設定。

```php theme={null}
// config/nostr.php

'driver' => env('NOSTR_DRIVER', 'node'),
```

```dotenv theme={null}
NOSTR_DRIVER=native
```

若不指定 driver 則使用預設 driver。

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

Nostr::event()->list();
```

也可以明確指定 driver。

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

Nostr::driver('node')->event()->list();
Nostr::node()->event()->list();

Nostr::driver('native')->event()->list();
Nostr::native()->event()->list();
```

## 安裝

<Steps>
  <Step title="安裝套件">
    ```bash theme={null}
    composer require revolution/laravel-nostr
    ```
  </Step>

  <Step title="發佈設定檔">
    ```bash theme={null}
    php artisan vendor:publish --tag=nostr-config
    ```
  </Step>
</Steps>

## Key 管理

### 產生 Key

```php theme={null}
use Revolution\Nostr\Facades\Nostr;
use Illuminate\Http\Client\Response;

/** @var Response $response */
$response = Nostr::key()->generate();
$keys = $response->json();
// [
//     'sk'   => 'sk...',
//     'nsec' => 'nsec...',
//     'pk'   => 'pk...',
//     'npub' => 'npub...',
// ]
```

### 轉換 Key

從 nsec 轉換。

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

$response = Nostr::key()->fromNsec(nsec: 'nsec');
$keys = $response->json();
// ['sk' => '...', 'nsec' => '...', 'pk' => '...', 'npub' => '...']
```

從 Secret Key 轉換。

```php theme={null}
$response = Nostr::key()->fromSecretKey(sk: 'sk');
```

從 npub 轉換(僅公鑰)。

```php theme={null}
$response = Nostr::key()->fromNpub(npub: 'npub');
$keys = $response->json();
// ['pk' => '...', 'npub' => '...']
```

從 Public Key 轉換。

```php theme={null}
$response = Nostr::key()->fromPublicKey(pk: 'pk');
```

## 取得事件

### 取得多筆事件

```php theme={null}
use Illuminate\Http\Client\Response;
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Filter;
use Revolution\Nostr\Kind;

$filter = Filter::make(
    authors: ['my pk'],
    kinds: [Kind::Text],
    limit: 10,
);

/** @var Response $response */
$response = Nostr::event()->list(filter: $filter);
$events = $response->json('events');
// [
//     ['id' => '...1', 'kind' => 1, 'content' => '...'],
//     ['id' => '...2', 'kind' => 1, 'content' => '...'],
// ]
```

### 取得單一事件

```php theme={null}
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Filter;
use Revolution\Nostr\Kind;

$filter = Filter::make(
    authors: ['my pk'],
    kinds: [Kind::Metadata],
);

$response = Nostr::event()->get(filter: $filter);
$event = $response->json('event');
// ['id' => '...', 'kind' => 0, 'content' => '{name: ""}']
```

## 發佈事件

### 發佈至單一 relay

```php theme={null}
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Event;
use Revolution\Nostr\Kind;

$event = Event::make(
    kind: Kind::Text,
    content: 'hello',
    created_at: now()->timestamp,
    tags: [],
);

$sk = 'my sk';

$response = Nostr::event()->publish(event: $event, sk: $sk);

if ($response->successful()) {
    $event = $response->json('event');
}
```

### 發佈至多個 relay(pool)

```php theme={null}
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Event;
use Revolution\Nostr\Kind;

$event = Event::make(
    kind: Kind::Text,
    content: 'test',
    created_at: now()->timestamp,
    tags: [],
);

$responses = Nostr::pool()->publish(event: $event, sk: 'my sk');
// $responses 是 array<string, Response>
// ['wss://relay1' => $response, 'wss://relay2' => $response]

foreach ($responses as $relay => $response) {
    if ($response->failed()) {
        dump($relay . ' : ' . $response->body());
    }
}
```

## Relay 伺服器設定

### 使用的 Relay 伺服器

僅使用 `Nostr::event()` 時,會使用 `config/nostr.php` 中的第一個 relay。使用 `Nostr::pool()` 時,設定中的所有 relay 都會列為目標。

### 執行時變更 Relay

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

$response = Nostr::event()->withRelay('wss://')->...;
```

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

$response = Nostr::pool()->withRelays(['wss://', 'wss://'])->...;
```

## NIP-05 Profile

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

$profile = Nostr::nip05()->profile('user@localhost');
// [
//     'user'   => 'user@localhost',
//     'pubkey' => 'pk',
//     'relays' => [],
// ]
```

## NIP-17 Private Direct Messages

<Info>
  NIP-17 僅支援 `native` driver。
</Info>

### 傳送私訊

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

$response = Nostr::driver('native')
    ->nip17()
    ->sendDirectMessage(
        sk: 'sender-secret-key',
        pk: 'receiver-public-key',
        message: 'Hello, this is a private message!'
    );
```

### 解密私訊

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

$response = Nostr::driver('native')
    ->nip17()
    ->decryptDirectMessage(
        giftWrap: $receivedGiftWrap,
        sk: 'receiver-secret-key'
    );

$decryptedMessage = $response->json();
```

## Laravel Notifications

使用 `NostrChannel` 可從 Laravel Notifications 向 Nostr 傳送訊息。

### Notification 類別

```php theme={null}
use Illuminate\Notifications\Notification;
use Revolution\Nostr\Notifications\NostrChannel;
use Revolution\Nostr\Notifications\NostrMessage;
use Revolution\Nostr\Tags\HashTag;

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

    public function toNostr(object $notifiable): NostrMessage
    {
        return new NostrMessage(
            // content 中的 #laravel 是顯示用,tags 的 HashTag 為協定層級的分類用
            content: 'hello #laravel',
            tags: [
                HashTag::make(t: 'laravel'),
            ],
        );
    }
}
```

### On-Demand 通知

```php theme={null}
use Illuminate\Support\Facades\Notification;
use Revolution\Nostr\Notifications\NostrRoute;

Notification::route('nostr', NostrRoute::to(sk: 'sk'))
    ->notify(new TestNotification());
```

### 整合至 User 模型

```php theme={null}
use Illuminate\Notifications\Notifiable;
use Revolution\Nostr\Notifications\NostrRoute;

class User
{
    use Notifiable;

    public function routeNotificationForNostr($notification): NostrRoute
    {
        return NostrRoute::to(sk: $this->sk, relays: ['wss://']);
    }
}
```

```php theme={null}
$user->notify(new TestNotification());
```

### 通知使用的 Relay 伺服器

預設會使用 `config/nostr.php` 中所有 relay。以 `NostrRoute` 指定 relay 則可在執行時變更。

```php theme={null}
use Revolution\Nostr\Notifications\NostrRoute;

return NostrRoute::to(sk: 'sk', relays: ['wss://', 'wss://']);
```

<Info>
  最新資訊請參閱 [GitHub 儲存庫](https://github.com/invokable/laravel-nostr)。
</Info>


## Related topics

- [我的包](/zh-CN/packages/index.md)
- [Laravel Telescope](/zh-TW/telescope.md)
- [Laravel Bluesky](/zh-TW/packages/laravel-bluesky/index.md)
- [Laravel Octane](/zh-TW/octane.md)
- [Laravel Boost](/zh-TW/boost.md)
