# クイックスタート

## 1. 接続先とトークンを手に入れる

字幕を受け取るには、**Subscribe の URL**（`wss://<host>/v1/subscribe`）と、**`transcript:read` のセッショントークン**の 2 つが要ります。今すぐ試すなら A で、続けて使うサーバー側のコードを書くなら B で、URL とトークンを手に入れます。

### A. Discord のリンクから（今すぐ試す） {#from-discord}

A の方法は、文字起こしが動いている VC で使えます。文字起こしが動いている VC は、誰かが `/start` で文字起こしを始めた VC か、自動入室でボットが入っている VC です。

1. Discord で `/link` を実行し、返答の「配信用オーバーレイ（OBS 用）」のボタンを押します。
2. 開いたブラウザのアドレス欄の URL をコピーします。URL は `https://moji.kaitoranma.com/#token=…&overlay=1` の形です。URL に `#token=` が入っていれば、トークンを取れています。
3. コピーした URL から、接続先とトークンを取り出します。

```js
// オーバーレイの URL から接続先とトークンを取り出す
const overlay = new URL("https://moji.kaitoranma.com/#token=eyJ2Ijox...&overlay=1");
const token = new URLSearchParams(overlay.hash.slice(1)).get("token");
const url = `${overlay.protocol === "https:" ? "wss:" : "ws:"}//${overlay.host}/v1/subscribe`;
```

Discord のリンクのトークンには期限があります。Discord のリンクのトークンでは、リンクを取ったときに動いている文字起こしの字幕だけを受け取れます。

### B. API キーから（続けて使う・サーバー側のコード） {#from-api-key}

あなたは API キー（`mjk_live_…`、スコープ `transcripts:read`）で、動いている文字起こし（セッション）を探して、トークンを発行します。

API キーを作る手順:

1. Discord サーバーの管理者（「サーバーの管理」か「管理者」の権限を持つ人、または所有者）が、文字起こしを使う Discord サーバーに、文字おこしくんのボットを追加します（[Discord に追加](/invite)）。
2. その Discord サーバーの管理者が、[管理画面](/app/)に Discord でログインします。
3. その Discord サーバーの管理者が、管理画面でその Discord サーバーを選び、「開発者向け設定」（`https://moji.kaitoranma.com/app/guilds/<サーバー ID>/keys`）でキーを作ります。管理画面は作ったキーを 1 回だけ表示します。

あなたがその Discord サーバーの管理者でなければ、その Discord サーバーの管理者に API キーを作ってもらうか、A で試してください。

**API キーはソースに書かず、ブラウザに渡さないでください。** ブラウザには、発行したトークンだけを渡してください。

```sh
BASE=https://moji.kaitoranma.com
KEY=$MOJIOKOSHI_API_KEY   # mjk_live_…（ソースに書かず、環境変数などから読む）

# 動いているセッションを新しい順に一覧し、先頭の sessionId を取り出す（jq を使う例）
curl -sS "$BASE/v1/sessions?status=active&limit=5" -H "Authorization: Bearer $KEY"
SID=$(curl -sS "$BASE/v1/sessions?status=active&limit=1" -H "Authorization: Bearer $KEY" | jq -r '.sessions[0].sessionId')

# そのセッションの transcript:read のトークンを発行する（ttlSeconds は 1〜86400、既定 3600）
curl -sS -X POST "$BASE/v1/sessions/$SID/tokens" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"scope":"transcript:read","ttlSeconds":3600}'
# → {"token":"eyJ2Ijox…","expiresAt":1790355000000}
```

API キーでセッションを探してトークンを発行する TypeScript・Python の例は、[HTTP API リファレンス](http.md#examples)の最後にあります。

## 2. 受け取る

依存なしの参照実装 [examples/mojiokoshi-subscribe.js](examples/mojiokoshi-subscribe.js)（ブラウザ / Node 22 以降）をダウンロードして、次のコード（quickstart.mjs）と同じフォルダに置きます。参照実装は再接続・生存確認・改訂の置き換え・再接続せずに終えるべきエラーの扱いまで実装しています。

```js
// node quickstart.mjs <wss://…/v1/subscribe> <token>
import { subscribe, speakerName } from "./mojiokoshi-subscribe.js";

const [url, token] = process.argv.slice(2);
subscribe({
  url,
  token,
  onChange(state, message) {
    // 確定した発話だけを出す。同じ utteranceId の改訂（refined）は置き換えとして届く。
    if (message.type === "utterance" && message.utterance.stage !== "partial") {
      console.log(`${speakerName(state, message.utterance)}: ${message.utterance.text}`);
    }
  },
  onStatus(status, detail) {
    console.error(`[${status}]`, detail?.code ?? "");
    if (status === "ended" || status === "failed") process.exitCode = status === "failed" ? 1 : 0;
  },
});
```

プログラムが `[open]` と出した後に VC で誰かが話すと、プログラムは、確定した発言を `たろう: こんにちは。` のように 1 行ずつ出します。発言が出ないとき:

- `[failed] unauthorized` と出た: トークンの期限が切れています。1 の手順でトークンを取り直してください（[トークンの期限](reconnection.md#token-expiry)）。
- `[ended]` と出た: トークンを取った文字起こしは、もう終わっています。文字起こしを始め直してから、1 に戻ってください。

参照実装はブラウザでも `<script type="module">` から読み込めます。1 ファイルで動く HTML は、[オーバーレイのガイド](guides/overlay.md)にあります。

### Python（websockets）

[examples/mojiokoshi_subscribe.py](examples/mojiokoshi_subscribe.py) は、確定した発話を 1 行ずつ出す参照実装です。

```sh
pip install websockets
python mojiokoshi_subscribe.py "wss://moji.kaitoranma.com/v1/subscribe" "eyJ2Ijox…"
# たろう: こんにちは、今日はよろしくお願いします。
# はなこ: よろしくお願いします。
```

### プロトコルを直接書く場合の最小の流れ

```text
→ {"type":"hello","protocolVersion":1,"token":"eyJ2Ijox…"}
← {"type":"welcome","sessionId":"…",…}
← {"type":"utterance","utterance":{"utteranceId":"utt_02","stage":"partial","revision":1,"text":"それでは始め",…}}
← {"type":"utterance","utterance":{"utteranceId":"utt_02","stage":"final","revision":3,"text":"それでは始めましょう",…}}
← {"type":"utterance","utterance":{"utteranceId":"utt_02","stage":"refined","revision":4,"text":"それでは始めましょう。",…}}
→ {"type":"ping"}
← {"type":"pong"}
← {"type":"session_ended"}
```

クライアントは、`{"type":"ping"}` を 15 秒ごとに送ります。

クライアントは、同じ `utteranceId` の発話を**置き換え**（`revision` が大きい方を採る）、接続が切れたら**バックオフして再接続**します。クライアントには置き換えと再接続の 2 つの処理が欠かせません。メッセージの一覧は [WebSocket API リファレンス](reference.md)にあります。

## 3. 終わった後に書き出す（curl）

あなたはセッションが終わった後も、保持期間（Discord サーバーのレベルにより 30〜365 日）の間はセッションの記録を書き出せます。書き出しには、`transcript:read` のトークン（または `transcripts:read` の API キー）を使います。

```sh
# テキストで書き出す（時刻のタイムゾーンは tz で指定する。既定は Asia/Tokyo）
curl -s "$BASE/v1/sessions/$SID/transcript?format=text&tz=Asia/Tokyo" -H "Authorization: Bearer $TOKEN"
# [21:03:05] たろう: こんにちは。
# [21:03:09] はなこ: よろしくお願いします。

# JSON で書き出す
curl -s "$BASE/v1/sessions/$SID/transcript" -H "Authorization: Bearer $TOKEN"
```

JSON の形と、記録として残すときの注意は、[記録と書き出し](guides/archive.md)にあります。

## 次に読む

- [発話の改訂モデル](revisions.md): `partial` / `final` / `refined` の扱い
- [再接続とエラーの扱い](reconnection.md): 再接続せずに終えるべきエラーとバックオフ
- 作りたいものに合わせたガイド: [オーバーレイ](guides/overlay.md) / [ボットへの中継](guides/bot-relay.md) / [記録と書き出し](guides/archive.md) / [分析](guides/analytics.md)
- [Webhook リファレンス](webhooks.md): 文字起こしの開始と終了の知らせの受け取り方（あなたのサーバーが一覧を読み直さずに新しいセッションを知る方法）

## コーディングエージェント向けのプロンプト

```text
文字おこしくん（Discord VC のリアルタイム文字起こし）の Subscribe WebSocket API に接続して、確定した発話を標準出力に出すクライアントを書いてください。

仕様: https://moji.kaitoranma.com/docs/llms.txt と、そこからリンクされている asyncapi.yaml / JSON Schema を読んでから実装すること。
- 参照実装 https://moji.kaitoranma.com/docs/examples/mojiokoshi-subscribe.js をそのまま使うか、同じ決まりで実装する。
- 接続先 URL とトークンは環境変数（MOJIOKOSHI_SUBSCRIBE_URL / MOJIOKOSHI_TOKEN）から読む。トークンはソースに書かない。
- 接続後に {"type":"hello","protocolVersion":1,"token":…} を送る。welcome を受け取ったら状態を丸ごと置き換える。
- utterance は utteranceId ごとに revision が最大のものだけを残す（追記しない）。partial は出さず、final と refined を出す（refined は同じ発話の修正版）。
- 15 秒ごとに {"type":"ping"} を送る。切断されたら 0.5 秒から倍々・上限 30 秒・ジッター付きで再接続する。
- unauthorized / protocol_version_unsupported / session_ended / superseded / quota_exceeded、fatal な invalid_message、session_ended メッセージでは再接続せずに終了する。
- 受け取ったメッセージは subscribe-server-message.json の JSON Schema で検証できるようにテストを書く。
```
