# Twitter 予約投稿・検索・アカウント管理

## 概要

Twitter（X）への予約投稿管理・ツイート検索・**投稿アカウント管理**を行う機能。2026-04-17 のアップデートで複数アカウント対応になり、ユーザー OAuth 連携した複数アカウントを切り替えて投稿できるようになった。

## URL 一覧

| URL | 機能 |
|-----|------|
| `/twitter` | 予約投稿の作成・一覧・編集・削除 |
| `/twitter/search` | キーワードでツイート検索、検索履歴、保存済みツイートのフルテキスト検索 |
| `/admin/twitter-posting-accounts` | 投稿アカウント一覧（admin専用） |
| `/admin/twitter-posting-accounts/create` | 投稿アカウント新規登録 |
| `/admin/twitter-posting-accounts/{id}/edit` | 投稿アカウント編集 |
| `/admin/twitter-posting-accounts/oauth/start` | OAuth 1.0a 連携開始 |
| `/admin/twitter-posting-accounts/oauth/callback` | OAuth コールバック（Developer Portal に要登録） |

## 使い方

### 予約投稿

- 投稿内容の作成・編集（280 文字以内、未来日時のみ）
- 送信アカウントを「投稿アカウント」から選択（未選択時は `.env` のデフォルト認証情報を使用）
- 予約投稿一覧の管理（編集・削除）
- スケジューラが予約時刻に達した投稿を検出し、Twitter API 経由で送信

### ツイート検索

- キーワード検索（`/tweets/search/recent`、直近 7 日）
- 検索結果の保存、保存済みツイートはフルテキスト検索可

### 投稿アカウント管理（2026-04-17 追加）

- **OAuth 連携**: `.env` のコンシューマーキーをアプリ認証として共有し、各ユーザーの access_token のみ DB に暗号化保存
- **手動入力**: アカウントごとに独自の API Key/Secret を入れることも可（スタンドアロンアプリ用）
- **暗号化**: `api_key` / `api_secret` / `access_token` / `access_token_secret` は Eloquent の `encrypted` cast で保存時に暗号化
- **有効/無効切替**: アカウントごとに `is_active` フラグ

## 技術構成

### Controllers

| クラス | 役割 |
|--------|------|
| `App\Http\Controllers\Content\TwitterController` | 予約投稿 CRUD・検索処理 |
| `App\Http\Controllers\Admin\TwitterPostingAccountController` | 投稿アカウント管理・OAuth フロー |

### Models

| クラス | 役割 |
|--------|------|
| `App\Models\Social\TwitterScheduledPost` | 予約投稿データ（`twitter_posting_account_id` で送信アカウント紐付け） |
| `App\Models\Social\TwitterPostingAccount` | 投稿アカウント（認証情報は暗号化保存） |

### Services

| クラス | 役割 |
|--------|------|
| `App\Services\Social\Twitter\TwitterApiService` | Twitter API v2 クライアント。`useAccount()` で使用アカウント切替 |
| `App\Services\Social\Twitter\TwitterScheduledPostService` | 予約投稿の送信処理（`twitter_posting_account` を参照して認証情報を差し替え） |

### 認証情報の優先順位

`TwitterApiService::useAccount()` は以下の優先順で認証情報を選択する:

1. `TwitterPostingAccount` の `api_key` / `api_secret` / `access_token` / `access_token_secret`（値があれば）
2. `.env` のデフォルト（`TWITTER_CONSUMER_KEY` / `TWITTER_CONSUMER_SECRET` / `TWITTER_ACCESS_TOKEN` / `TWITTER_ACCESS_TOKEN_SECRET`）

これにより、App 認証は `.env` で共有し、ユーザー認証だけを DB に持たせる OAuth 連携モデルと、アカウントごとに完全独立で持たせるモデルの両方に対応できる。

### 処理フロー（予約投稿）

1. ユーザーが投稿内容と日時、送信アカウントを設定
2. `TwitterScheduledPost` としてDB保存
3. スケジューラ（`schedule:run`）が予約時刻に達した投稿を検出
4. `TwitterScheduledPostService` が `twitter_posting_account` を取得し `TwitterApiService::useAccount()` で認証差し替え
5. Twitter API 経由で投稿を実行

## DB テーブル

| テーブル | 役割 |
|---------|------|
| `twitter_scheduled_posts` | 予約投稿。`twitter_posting_account_id` で送信アカウントを指定（NULL 時は `.env` 使用） |
| `twitter_posting_accounts` | 投稿アカウント。認証情報は暗号化カラム |

主なマイグレーション:
- `2026_04_16_120000_create_twitter_posting_accounts_table.php`
- `2026_04_16_130000_add_account_id_to_twitter_scheduled_posts_table.php`

## 必要な環境変数

変数名は X Developer Portal の UI ラベル（コンシューマーキー／アクセストークン）に合わせています。

| 変数名 | 対応する Portal ラベル | 備考 |
|--------|----------------------|------|
| `TWITTER_CONSUMER_KEY` | コンシューマーキー (Consumer Key / API Key) | 旧 `TWITTER_API_KEY` もフォールバック可 |
| `TWITTER_CONSUMER_SECRET` | コンシューマーシークレット | 旧 `TWITTER_API_SECRET` もフォールバック可 |
| `TWITTER_ACCESS_TOKEN` | アクセストークン | 単一アカウント運用時のデフォルト |
| `TWITTER_ACCESS_TOKEN_SECRET` | アクセストークンシークレット | 単一アカウント運用時のデフォルト |

## OAuth 1.0a 連携手順

1. X Developer Portal で App を作成し、Callback URL に `/admin/twitter-posting-accounts/oauth/callback` の絶対 URL を登録
2. `.env` に `TWITTER_CONSUMER_KEY` / `TWITTER_CONSUMER_SECRET` を設定
3. `/admin/twitter-posting-accounts` → 「OAuth 連携」ボタン → 認可画面で承諾
4. コールバック後、`access_token` / `access_token_secret` が暗号化保存される（既存 `screen_name` 一致時は更新）
