# VRC_OSC

VRChat の OSC パラメータを受けて、Discord ミュート、VRChat ログ監視、OCR、翻訳を実行するローカルツール。

## 起動

依存関係を入れる。

```powershell
python -m pip install -r requirements.txt
```

PowerShell から起動する。

```powershell
bin\vrc_osc.ps1
```

Python から直接起動する場合。

```powershell
python src\bin\vrc_osc.py
```

OSC は `127.0.0.1:9001` で待ち受ける。

## 構成

```text
bin/                 PowerShell 起動スクリプト
config/              example とローカル設定置き場
src/app.py           アプリ本体のエントリ
src/bin/             Python 起動スクリプト
src/common/          パス、設定ロード、runtime ログなどの共通基盤
src/discord_control/ Discord ウィンドウ操作
src/ocr/             OCR 実行
src/osc/             OSC gateway
src/screen/          VRChat ウィンドウ検出とキャプチャ
src/tools/           単体実行用の補助ツール
src/translate/       翻訳
src/vrc_log/         VRChat ログ解析と名簿照合
```

## 設定ファイル

実設定は `config/config.toml` に置く。このファイルは Git に入れない。

雛形は `config/config.example.toml`。

```toml
[self]
name = "自分のVRChat表示名"

[staff]
names = [
  "StaffUserA"
]

[guest]
file = "config/guests.txt"

[notice]
missing_count = 3

[ocr.crop]
left = 0.05
top = 0.35
right = 0.95
bottom = 0.95

[ocr]
preprocess = true
scale = 1.5
use_angle_cls = false
```

`self.name` は Discord 自動ミュート判定に使う。

`staff.names` は表示対象だが guest 人数には含めない。

`guest.file` は guest 名簿ファイル。guest 人数の母数と在室判定に使う。

`notice.missing_count` は「あと何人以内で全員集合か」の通知範囲。例えば guest が 23 人で `missing_count = 3` の場合、20〜22 人のときに未入室 guest を出す。

OCR 設定は速度と精度の調整用。

`ocr.crop` は VRChat ウィンドウ内の相対座標。`0.0` から `1.0` で指定する。デフォルトは画面下側の中央寄りだけを読む。

フル画面 OCR に戻す場合。

```toml
[ocr.crop]
left = 0
top = 0
right = 1
bottom = 1
```

`ocr.preprocess = true` はグレースケール化、コントラスト補正、シャープ化、拡大を行う。

`ocr.scale` は前処理時の拡大率。大きいほど小さい文字に強くなるが遅くなる。

`ocr.use_angle_cls = false` は角度分類を切って高速化する。縦書きや回転文字を読む必要がある場合だけ `true` にする。

## guest 名簿

`config/guests.txt` に 1 行 1 名で書く。このファイルは Git に入れない。

雛形は `config/guests.example.txt`。

```text
GuestUserA
GuestUserB
GuestUserC
```

空行と `#` で始まる行は無視する。

## 秘密情報

秘密情報は `config/secrets.toml` に置く。このファイルは Git に入れない。

雛形は `config/secrets.example.toml`。

```toml
[discord]
webhook_url = ""

[vrchat]
username = ""
password = ""
```

`discord.webhook_url` は VRC ログ結果の Discord Webhook 通知に使う。

`vrchat.username` / `vrchat.password` は `src/tools/resolve_vrchat_user.py` を使う場合だけ必要。

## OSC パラメータ

Avatar Parameters 側の名前と一致させる。

| Parameter | 動作 |
| --- | --- |
| `DiscordSend` | 押した瞬間に Discord の mute hotkey を送る |
| `vrc_log` | VRC ログ監視の on/off |
| `ocrEnabled` | VRChat 画面を撮って OCR |
| `translation` | OCR して日本語翻訳 |

## VRC ログ監視

`vrc_log` を押すと、現在の VRChat ログを一度解析して Discord Webhook に送る。その後はログファイル末尾を追い、新しい join / leave / world 系ログが追記されたタイミングで即再解析する。

以前のような `output_log = 10` の秒数ポーリング設定は使わない。

解析対象は昼 12:00 以降。実行時刻が昼 12:00 前なら前日 12:00 以降を見る。

インスタンスが変わったら在室状態はリセットする。

出力例。

```text
ワールド入室: Midnight Rooftop
現在Guest: 0/3
[01:20] [join] GuestUserA 1/3
[01:21] [join] GuestUserB 2/3
[01:25] [leave] GuestUserA 1/3
[01:26] [join] [staff] StaffUserA 1/3
```

notice 条件に入った場合。

```text
現在Guest: 2/3
未入室Guest:
- GuestUserC
```

## Discord 自動ミュート

常時 self monitor が VRChat ログの新規追記だけを見る。

新しく追記されたログの中で、自分の最後のイベントが `OnPlayerJoined` なら Discord を mute、`OnPlayerLeft` なら unmute する。

過去ログ全体の最後が self leave / join だっただけでは反応しない。

`setDiscordMuted()` にはロックがあり、同時実行で mute hotkey が連続送信されないようにしている。

## OCR / 翻訳

`ocrEnabled` は VRChat ウィンドウを撮影して PaddleOCR にかける。結果はクリップボードへコピーする。

`translation` は OCR 結果を翻訳し、翻訳結果をクリップボードへコピーする。

OCR はデフォルトで画面全体ではなく、画面下側の中央寄りを切り出して読む。会話ログや字幕を読む用途では速く、余計な UI 文字を拾いにくい。

## runtime

新規の実行ログは基本的に以下へまとめる。

```text
runtime/runtime.log
runtime/latest_ocr.png
runtime/latest_screenshot.png
```

`runtime/` は Git に入れない。

OCR text、翻訳 text、VRC ログ結果は `runtime/runtime.log` に追記する。

画像は最新ファイルに上書きする。日時別ファイルを増やさない。

## ユーザー名照合

`src/vrc_log/member_name_matcher.py` は TOML 登録名と VRChat 表示名を安全な順番で紐づける補助実装。

照合順。

1. 完全一致
2. 正規化一致
3. 高信頼 fuzzy
4. guest のみ低信頼 fuzzy
5. pending / ambiguous / unmatched

完全一致・正規化一致で確定した名前は fuzzy 候補から除外する。

## Git に入れないもの

以下はローカル専用。

- `config/config.toml`
- `config/secrets.toml`
- `config/guests.txt`
- `runtime/`
- `__pycache__/`
- `*.pyc`
- `.vrchat_cookie.txt`

秘密情報を含む可能性があるものはコミットしない。
