# 機能設計書 47-メッセージ引用

## 概要

本ドキュメントは、Chatwootにおける返信時の元メッセージ参照（メッセージ引用）機能の設計を記述します。

### 本機能の処理概要

メッセージ送信時に、返信先となる元メッセージを指定する機能を提供します。引用情報はcontent_attributesのin_reply_toおよびin_reply_to_external_idに保存され、UIで元メッセージとの関連を表示したり、外部チャネル（Twitter、WhatsAppなど）でスレッド形式の返信を実現します。

**業務上の目的・背景**：会話内で複数のトピックが並行する場合や、特定のメッセージに対して明確に返信したい場合に、メッセージ引用機能が有用です。特にTwitterのスレッド返信やWhatsAppの引用返信など、外部チャネルの引用機能と連携することで、チャネル固有のユーザー体験を維持できます。

**機能の利用シーン**：
- エージェントが特定の顧客メッセージに対して返信する場面
- Twitterでスレッド形式の返信を行う場面
- WhatsAppで引用返信を行う場面
- 会話内の複数トピックを整理する場面

**主要な処理内容**：
1. 返信先メッセージIDの受信（内部IDまたは外部ID）
2. InReplyToMessageBuilderによる参照解決
3. content_attributesへの引用情報保存
4. 外部チャネルへの引用情報送信

**関連システム・外部連携**：
- 各チャネルAPI（Twitter、WhatsApp等）の引用返信機能
- Message content_attributes

**権限による制御**：メッセージ送信権限を持つユーザーが引用機能を利用できます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 主画面 | 返信時の元メッセージ参照 |

## 機能種別

データ処理 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content_attributes.in_reply_to | Integer | No | 返信先メッセージの内部ID | 存在するメッセージID |
| content_attributes.in_reply_to_external_id | String | No | 返信先メッセージの外部ID（source_id） | - |

### 入力データソース

- 画面入力：会話詳細画面のメッセージ返信UI
- API：メッセージ作成時のcontent_attributesパラメータ
- 外部Webhook：チャネルからの引用情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content_attributes.in_reply_to | Integer | 返信先メッセージの内部ID |
| content_attributes.in_reply_to_external_id | String | 返信先メッセージの外部ID（source_id） |

### 出力先

- データベース：messagesテーブルのcontent_attributes
- 外部チャネル：各チャネルの引用返信機能

## 処理フロー

### 処理シーケンス

```
1. メッセージ作成時
   └─ MessageBuilder経由でin_reply_to/in_reply_to_external_idを受信
2. 保存前処理
   └─ before_save :ensure_in_reply_toが呼び出される
3. InReplyToMessageBuilder初期化
   └─ message, in_reply_to, in_reply_to_external_idをパラメータに
4. 参照解決
   └─ in_reply_to_messageで参照先メッセージを検索
5. 属性設定
   └─ in_reply_to_external_idにsource_idを、in_reply_toにidを設定
6. メッセージ保存
   └─ content_attributesに引用情報が含まれる
7. チャネル配信
   └─ SendReplyJobで外部チャネルに引用情報を送信
```

### フローチャート

```mermaid
flowchart TD
    A[メッセージ作成リクエスト] --> B{in_reply_to指定あり?}
    B -->|No| C{in_reply_to_external_id指定あり?}
    C -->|No| D[引用なしで保存]
    B -->|Yes| E[InReplyToMessageBuilder実行]
    C -->|Yes| E
    E --> F{参照先メッセージ検索}
    F -->|内部ID指定| G[conversation.messages.find_by id]
    F -->|外部ID指定| H[conversation.messages.find_by source_id]
    G --> I[content_attributes設定]
    H --> I
    I --> J[in_reply_to = message.id]
    J --> K[in_reply_to_external_id = message.source_id]
    K --> L[メッセージ保存]
    L --> M[チャネル配信]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-47-001 | 内部ID優先 | in_reply_toが指定されている場合は内部IDで検索 | in_reply_to.present? |
| BR-47-002 | 外部IDフォールバック | in_reply_to_external_idが指定されている場合はsource_idで検索 | in_reply_to.blank? && in_reply_to_external_id.present? |
| BR-47-003 | 同一会話内限定 | 引用先メッセージは同一会話内のみ | 常に適用 |
| BR-47-004 | 存在しない場合 | 参照先が見つからない場合はnilが設定される | find_byでnilが返る場合 |

### 計算ロジック

特に複雑な計算ロジックはありません。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 引用メッセージ作成 | messages | INSERT | content_attributesにin_reply_to情報を含む |

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

#### messages

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | content_attributes | {in_reply_to: id, in_reply_to_external_id: source_id} | JSON |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 参照不可 | 指定されたIDのメッセージが見つからない | in_reply_to/in_reply_to_external_idがnilになる（エラーではない） |

### リトライ仕様

特にリトライ処理は実装されていません。

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

メッセージ作成トランザクション内で引用情報の解決・設定が行われます。

## パフォーマンス要件

- conversation.messages.find_byによる単一レコード検索
- 同一会話内のメッセージに限定されるため、検索範囲は限定的

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

- 同一会話内のメッセージのみ参照可能
- 会話へのアクセス権限チェック（親機能で実施）

## 備考

- Twitterのin_reply_to_tweet_idとの連携で使用されます
- WhatsAppの引用返信機能との連携で使用されます
- UIでは引用元メッセージのプレビューが表示されます

---

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

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

### 推奨読解順序

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

まず、引用情報の保存先を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | message.rb | `app/models/message.rb` | content_attributes store（109-111行目）、in_reply_to/in_reply_to_external_idアクセサ |

**読解のコツ**: content_attributesのstoreアクセサとしてin_reply_toとin_reply_to_external_idが定義されています。

#### Step 2: エントリーポイントを理解する

MessageBuilderでの引用情報の取り扱いを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | message_builder.rb | `app/builders/messages/message_builder.rb` | @in_reply_toの初期化（19行目） |
| 2-2 | message.rb | `app/models/message.rb` | before_save :ensure_in_reply_to（68行目） |

**主要処理フロー**:
1. **19行目** (message_builder.rb): content_attributesからin_reply_toを取得
2. **68行目** (message.rb): before_saveでensure_in_reply_toを呼び出し

#### Step 3: 引用解決サービスを理解する

InReplyToMessageBuilderの処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | in_reply_to_message_builder.rb | `app/services/messages/in_reply_to_message_builder.rb` | 全体（1-25行目） |

**主要処理フロー**:
- **6-8行目**: performメソッドで引用情報の存在チェック
- **12-15行目**: set_in_reply_to_attributeで属性を設定
- **17-23行目**: in_reply_to_messageで参照先メッセージを検索

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

```
Messages::MessageBuilder#perform
    │
    └─ @conversation.messages.build(message_params)
           │
           └─ in_reply_to: @in_reply_to (from content_attributes)

Message#save (before_save callback)
    │
    └─ ensure_in_reply_to
           │
           └─ Messages::InReplyToMessageBuilder#perform
                  │
                  ├─ in_reply_to.present? || in_reply_to_external_id.present?
                  │
                  └─ set_in_reply_to_attribute
                         │
                         ├─ in_reply_to_message (検索)
                         │      ├─ find_by(id: @in_reply_to)
                         │      └─ find_by(source_id: @in_reply_to_external_id)
                         │
                         ├─ content_attributes[:in_reply_to] = message.id
                         │
                         └─ content_attributes[:in_reply_to_external_id] = message.source_id
```

### データフロー図

```
[入力]                    [処理]                              [出力]

in_reply_to         ───▶ MessageBuilder               ───▶ Message.new
in_reply_to_external_id                                     │
                                                            ▼
                                                      before_save
                                                            │
                                                            ▼
                                                      InReplyToMessageBuilder
                                                            │
                                                            ▼
                                                      conversation.messages.find_by
                                                            │
                                                            ▼
                                                      content_attributes設定
                                                            │
                                                            ▼
                                                      DB (messages.content_attributes)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| in_reply_to_message_builder.rb | `app/services/messages/in_reply_to_message_builder.rb` | サービス | 引用解決ロジック |
| message.rb | `app/models/message.rb` | モデル | 引用情報の保存、コールバック |
| message_builder.rb | `app/builders/messages/message_builder.rb` | ビルダー | メッセージ作成時の引用情報取り扱い |
