# 機能設計書 125-TikTok

## 概要

本ドキュメントは、ChatwootのTikTok DM連携機能の設計仕様を記載する。この機能は、TikTok Business APIを通じたダイレクトメッセージ（DM）をChatwootで一元管理するためのチャネル統合機能である。

### 本機能の処理概要

TikTok連携は、TikTok Business APIを通じてビジネスアカウントのダイレクトメッセージを受信し、Chatwootの会話として管理する。エージェントはChatwootからTikTokユーザーへの返信を直接送信できる。TikTok Business アカウントが必要。

**業務上の目的・背景**：TikTokは急成長するソーシャルメディアプラットフォームであり、特に若年層へのリーチにおいて重要な顧客接点となっている。本機能により、TikTok経由のDMをChatwootで統合管理でき、SNSマーケティングとカスタマーサポートの連携を強化する。

**機能の利用シーン**：
- 顧客からのTikTok DMを受信・返信する場面
- ブランドアカウントへの問い合わせに対応する場面
- インフルエンサーマーケティングのコミュニケーション管理

**主要な処理内容**：
1. TikTok Business APIとの認証・接続設定
2. Webhookを通じたDMの受信処理
3. TikTok APIを使用したDM送信（テキスト・メディア）
4. アクセストークン・リフレッシュトークンの自動更新
5. 連絡先（TikTokユーザー）の自動作成・更新

**関連システム・外部連携**：
- TikTok Business API（メッセージ送受信）
- TikTok Webhook（イベント受信）
- TikTok OAuth 2.0（認証）

**権限による制御**：インボックスの作成・管理にはAdministrator権限が必要。メッセージの送受信はインボックスにアサインされたエージェントが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 49 | チャネル設定画面 | 主画面 | TikTokチャネルの設定・認可フロー |
| 11 | 会話詳細画面 | 主画面 | TikTok DMの送受信 |

## 機能種別

データ連携 / メッセージング

## 入力仕様

### 入力パラメータ（チャネル作成時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| business_id | String | Yes | TikTok Business ID | 一意性 |
| access_token | String | Yes | アクセストークン | 必須 |
| refresh_token | String | Yes | リフレッシュトークン | 必須 |
| expires_at | DateTime | Yes | アクセストークン有効期限 | 必須 |
| refresh_token_expires_at | DateTime | Yes | リフレッシュトークン有効期限 | 必須 |
| account_id | Integer | Yes | Chatwootアカウント | 必須 |

### 入力パラメータ（メッセージ送信時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content | String | No | メッセージ本文 | テキストのみまたは添付ファイルのみ |
| attachments | Array | No | 添付ファイル | 単一ファイルのみ |

### 入力データソース

- TikTok OAuth認可フローからトークン取得
- Chatwoot会話画面からのメッセージ入力

## 出力仕様

### 出力データ（Webhook受信時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message | Message | 作成されたメッセージレコード |
| conversation | Conversation | 関連する会話 |
| contact | Contact | 送信者の連絡先 |

### 出力データ（メッセージ送信時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message_id | String | TikTok側のメッセージID（source_idとして保存） |

### 出力先

- Chatwootデータベース（messages, conversations, contacts）
- TikTok API（送信メッセージ）

## 処理フロー

### 処理シーケンス（メッセージ受信）

```
1. TikTok Webhookからイベント受信
   └─ TiktokController#eventsで処理
2. 署名検証
   └─ TIKTOK_APP_SECRETで署名を検証
3. エコーイベントの場合
   └─ 2秒遅延でジョブ実行（レース条件防止）
4. 通常イベントの場合
   └─ TiktokEventsJobで即座に処理
5. 連絡先・会話・メッセージの作成
```

### 処理シーケンス（メッセージ送信）

```
1. Chatwootでメッセージ作成
   └─ Message#after_create_commitコールバック
2. SendOnTiktokServiceの実行
   └─ メッセージサポート検証（添付ファイル制限）
   └─ トークン検証・リフレッシュ
3. TikTok APIへ送信
   └─ テキストまたはメディアメッセージ送信
4. レスポンス処理
   └─ message_idをsource_idとして保存
   └─ ステータスをdeliveredに更新
```

### フローチャート

```mermaid
flowchart TD
    A[メッセージ作成] --> B{チャネル検証}
    B -->|NG| C[処理終了]
    B -->|OK| D{添付ファイルあり?}
    D -->|Yes| E{テキストもあり?}
    E -->|Yes| F[エラー: テキスト+添付不可]
    E -->|No| G{複数添付?}
    G -->|Yes| H[エラー: 単一添付のみ]
    G -->|No| I[send_media_message]
    D -->|No| J[send_text_message]
    I --> K{送信成功?}
    J --> K
    K -->|Yes| L[source_id保存 & delivered]
    K -->|No| M[failed & エラーメッセージ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | テキスト・添付排他 | テキストと添付ファイルを同時に送信不可 | メッセージ送信時 |
| BR-002 | 単一添付制限 | 1メッセージに添付ファイルは1つのみ | メッセージ送信時 |
| BR-003 | トークンリフレッシュ | アクセストークン有効期限前に自動リフレッシュ | API呼び出し時 |
| BR-004 | 認可エラー閾値 | 1回の認可エラーで再認可フラグを立てる | API呼び出し時 |
| BR-005 | Webhook署名検証 | HMAC-SHA256で署名を検証、5秒以内のタイムスタンプ | Webhook受信時 |

### 計算ロジック

- Webhook署名計算: `HMAC-SHA256(client_secret, "#{timestamp}.#{request_payload}")`

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チャネル作成 | channel_tiktok | INSERT | TikTokチャネル情報保存 |
| トークン更新 | channel_tiktok | UPDATE | access_token, expires_at等更新 |
| メッセージ受信 | messages | INSERT | 受信メッセージ保存 |
| メッセージ送信 | messages | UPDATE | source_id, status更新 |
| 連絡先作成 | contacts | INSERT/UPDATE | 送信者情報保存 |

### テーブル別操作詳細

#### channel_tiktok

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | business_id, access_token, refresh_token, expires_at, refresh_token_expires_at, account_id | OAuth認可フローから取得 | 暗号化保存 |
| UPDATE | access_token, refresh_token, expires_at, refresh_token_expires_at | リフレッシュ後の新トークン | 有効期限更新 |
| SELECT | access_token | チャネルID指定 | API呼び出し時に使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| token_expired | Auth Error | アクセストークン期限切れ | 自動リフレッシュ |
| unauthorized | Auth Error | Webhook署名検証失敗 | 401エラー返却 |
| text_with_attachment | Validation Error | テキストと添付ファイル同時送信 | エラーメッセージ表示 |
| multiple_attachments | Validation Error | 複数添付ファイル送信 | エラーメッセージ表示 |

### リトライ仕様

- トークン期限切れ時は自動リフレッシュ後に再試行
- その他のエラーは自動リトライなし

## トランザクション仕様

- メッセージ受信時：メッセージ・会話・連絡先の作成は単一トランザクション
- トークンリフレッシュ：独立したトランザクション

## パフォーマンス要件

- Webhook処理はバックグラウンドジョブで実行
- エコーイベント（im_send_msg）は2秒遅延で処理

## セキュリティ考慮事項

- access_token、refresh_tokenは暗号化して保存
- Webhook署名検証（HMAC-SHA256）で不正リクエスト防止
- タイムスタンプ検証（5秒以内）でリプレイ攻撃防止
- TIKTOK_APP_SECRET環境変数で署名検証キーを管理

## 備考

- AUTHORIZATION_ERROR_THRESHOLD = 1（1回の認可エラーで再認可要求）
- Reauthorizableモジュールで認可エラー時の再認可フロー対応
- TokenServiceでトークン自動リフレッシュ

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tiktok.rb | `app/models/channel/tiktok.rb` | チャネルモデルの構造、トークン管理、暗号化設定 |

**読解のコツ**:
- `access_token`と`refresh_token`の両方を管理
- `validated_access_token`メソッドがTokenServiceを呼び出す

**主要処理フロー**:
- **19-22行目**: スキーマ構造とReauthorizable include
- **24-28行目**: 暗号化設定（access_token, refresh_token）
- **30行目**: AUTHORIZATION_ERROR_THRESHOLD = 1
- **32-36行目**: バリデーション
- **42-44行目**: validated_access_tokenメソッドでトークン検証・リフレッシュ

#### Step 2: メッセージ送信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | send_on_tiktok_service.rb | `app/services/tiktok/send_on_tiktok_service.rb` | メッセージ送信処理とバリデーション |
| 2-2 | client.rb | `app/services/tiktok/client.rb` | TikTok APIクライアント |

**主要処理フロー（SendOnTiktokService）**:
- **8-17行目**: perform_reply - バリデーション後にメッセージ送信、ステータス更新
- **19-23行目**: validate_message_support! - テキスト+添付不可、複数添付不可のチェック
- **25-33行目**: send_message - テキストまたはメディアメッセージ送信の分岐
- **36-38行目**: tiktok_client - Tiktok::Clientの生成

#### Step 3: Webhook受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tiktok_controller.rb | `app/controllers/webhooks/tiktok_controller.rb` | Webhook受信と署名検証 |
| 3-2 | message_service.rb | `app/services/tiktok/message_service.rb` | メッセージ処理サービス |

**主要処理フロー（TiktokController）**:
- **2行目**: verify_signature! before_action
- **4-14行目**: eventsアクション - エコーイベント判定と遅延処理
- **23-39行目**: verify_signature! - HMAC-SHA256署名検証とタイムスタンプチェック
- **42-47行目**: extract_signature_parts - 署名ヘッダーの解析
- **50-52行目**: echo_event? - im_send_msgイベント判定

#### Step 4: トークンリフレッシュを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | token_service.rb | `app/services/tiktok/token_service.rb` | トークンリフレッシュ処理 |
| 4-2 | auth_client.rb | `app/services/tiktok/auth_client.rb` | 認証APIクライアント |

### プログラム呼び出し階層図

```
[メッセージ送信フロー]
Message#after_create_commit
    │
    └─ SendReplyJob.perform_later
           │
           └─ Tiktok::SendOnTiktokService#perform
                  │
                  ├─ validate_target_channel
                  ├─ outgoing_message? チェック
                  ├─ invalid_message? チェック
                  │
                  └─ perform_reply
                         │
                         ├─ validate_message_support!
                         │      ├─ テキスト+添付チェック
                         │      └─ 複数添付チェック
                         │
                         └─ send_message
                                │
                                ├─ channel.validated_access_token
                                │      └─ TokenService#access_token
                                │
                                ├─ [添付あり] tiktok_client.send_media_message
                                │
                                └─ [テキストのみ] tiktok_client.send_text_message

[メッセージ受信フロー]
TikTok Webhook
    │
    └─ Webhooks::TiktokController#events
           │
           ├─ verify_signature! (HMAC-SHA256)
           │
           ├─ [エコー] TiktokEventsJob.set(wait: 2.seconds)
           │
           └─ [通常] TiktokEventsJob.perform_later
                  │
                  └─ Tiktok::MessageService
                         ├─ 連絡先作成/取得
                         ├─ 会話作成/取得
                         └─ メッセージ作成
```

### データフロー図

```
[送信フロー]
[Chatwoot]              [処理]                    [TikTok]

Message作成     ───▶ SendReplyJob
                      │
                      ▼
                SendOnTiktokService
                      │
                      ├─▶ validate_message_support!
                      │
                      └─▶ channel.validated_access_token
                             │
                             ├─▶ [期限切れ] TokenService
                             │      └─ 新トークン取得・保存
                             │
                             └─▶ [有効] そのまま使用
                      │
                      ▼
                Tiktok::Client
                      │
                      ├─▶ [テキスト] send_text_message
                      │
                      └─▶ [メディア] send_media_message
                      │
                      ▼
                TikTok Business API    ───▶ TikTok
                      │
                      ▼
                message_id受信          ◀───  API Response
                      │
                      ▼
                source_id保存 & delivered

[受信フロー]
[TikTok]                [処理]                    [Chatwoot]

Webhook Event   ───▶ TiktokController#events
                      │
                      ├─▶ verify_signature!
                      │      ├─ HMAC-SHA256検証
                      │      └─ タイムスタンプ検証
                      │
                      ├─▶ [エコー] 2秒遅延
                      │
                      └─▶ TiktokEventsJob
                             │
                             └─▶ MessageService
                                    ├─ Contact作成/取得
                                    ├─ Conversation作成/取得
                                    └─ Message作成
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tiktok.rb | `app/models/channel/tiktok.rb` | モデル | チャネル定義 |
| send_on_tiktok_service.rb | `app/services/tiktok/send_on_tiktok_service.rb` | サービス | メッセージ送信処理 |
| client.rb | `app/services/tiktok/client.rb` | サービス | TikTok APIクライアント |
| auth_client.rb | `app/services/tiktok/auth_client.rb` | サービス | 認証APIクライアント |
| token_service.rb | `app/services/tiktok/token_service.rb` | サービス | トークンリフレッシュ |
| message_service.rb | `app/services/tiktok/message_service.rb` | サービス | メッセージ処理 |
| read_status_service.rb | `app/services/tiktok/read_status_service.rb` | サービス | 既読ステータス処理 |
| tiktok_controller.rb | `app/controllers/webhooks/tiktok_controller.rb` | コントローラー | Webhook受信 |
| tiktok_events_job.rb | `app/jobs/webhooks/tiktok_events_job.rb` | ジョブ | Webhook処理 |
| authorizations_controller.rb | `app/controllers/api/v1/accounts/tiktok/authorizations_controller.rb` | コントローラー | OAuth認可 |
