# 機能設計書 82-テンプレート管理

## 概要

本ドキュメントは、Chatwootのメッセージテンプレート管理機能の設計を記載する。会話内で自動送信されるテンプレートメッセージ（グリーティング、営業時間外、自動解決、CSAT調査、メール収集）を管理する機能である。

### 本機能の処理概要

テンプレート管理機能は、顧客との会話において自動的に送信されるメッセージテンプレートを管理・実行する機能である。

**業務上の目的・背景**：カスタマーサポート業務において、頻出する定型メッセージ（挨拶、営業時間外案内、会話終了通知等）を自動化することで、エージェントの負担を軽減し、顧客対応の一貫性と迅速性を確保する必要がある。

**機能の利用シーン**：
- 新規会話開始時にウェルカムメッセージを自動送信
- 営業時間外に顧客からメッセージを受信した際の自動応答
- 会話が自動解決された際の通知メッセージ送信
- 会話終了後のCSAT（顧客満足度）調査メッセージ送信
- メールアドレス未登録の顧客に対するメール収集フォーム送信

**主要な処理内容**：
1. HookExecutionServiceによるテンプレート送信条件判定
2. 各テンプレートクラスによるメッセージ生成・送信
3. メッセージタイプ「template」としてメッセージ保存
4. インボックス設定に基づくテンプレート有効/無効の制御

**関連システム・外部連携**：インボックス設定、営業時間設定、CSAT設定との連携が行われる。

**権限による制御**：テンプレートメッセージは自動システム処理として送信され、ユーザー権限による制御は行われない。インボックス設定の変更にはadministrator権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 50 | インボックス設定画面 | 主画面 | ウェルカムメッセージ、営業時間外メッセージの設定 |
| 43 | CSATレポート画面 | 参照画面 | CSATサーベイ結果の確認 |

## 機能種別

メッセージ自動生成 / 条件判定処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| message | Message | Yes | トリガーとなるメッセージ | incoming messageであること |
| conversation | Conversation | Yes | 対象会話 | キャンペーン由来でないこと |

### 入力データソース

- メッセージ受信イベント（MessageTemplates::HookExecutionService）
- 会話ステータス変更イベント（自動解決時）
- 会話終了イベント（CSAT送信時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message | Message | 生成されたテンプレートメッセージ |
| message_type | String | 'template' 固定 |
| content | String | テンプレートメッセージ本文 |
| content_type | String | メッセージコンテンツ種別（input_csat, input_email等） |

### 出力先

- messagesテーブル（テンプレートメッセージとして保存）
- WebSocket経由でフロントエンドに配信

## 処理フロー

### 処理シーケンス

```
1. メッセージ受信イベント発火
   └─ HookExecutionService.perform呼び出し
2. 送信条件チェック
   └─ キャンペーン由来でないこと確認
   └─ 最後の受信メッセージが存在すること確認
   └─ 自動返信メールでないこと確認
3. 各テンプレート送信判定
   └─ 営業時間外メッセージ判定
   └─ グリーティングメッセージ判定
   └─ メール収集メッセージ判定
4. テンプレートメッセージ作成
   └─ トランザクション内でメッセージ保存
5. 例外発生時はExceptionTrackerで記録
```

### フローチャート

```mermaid
flowchart TD
    A[メッセージ受信] --> B{キャンペーン会話?}
    B -->|Yes| Z[処理終了]
    B -->|No| C{最後の受信メッセージあり?}
    C -->|No| Z
    C -->|Yes| D{自動返信メール?}
    D -->|Yes| Z
    D -->|No| E[trigger_templates実行]
    E --> F{営業時間外?}
    F -->|Yes| G[OutOfOfficeメッセージ送信]
    F -->|No| H{初回メッセージ?}
    H -->|Yes| I{グリーティング有効?}
    I -->|Yes| J[Greetingメッセージ送信]
    I -->|No| K{メール収集有効?}
    H -->|No| K
    J --> K
    G --> K
    K -->|Yes| L{メール未取得?}
    L -->|Yes| M[EmailCollectメッセージ送信]
    L -->|No| Z
    K -->|No| Z
    M --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-82-01 | キャンペーン除外 | キャンペーン由来の会話にはテンプレート送信しない | 全テンプレート |
| BR-82-02 | ツイート除外 | Twitter会話には営業時間外・グリーティング送信しない | OutOfOffice, Greeting |
| BR-82-03 | アクティブ会話除外 | 5分以内にエージェント応答がある場合は営業時間外送信しない | OutOfOffice |
| BR-82-04 | 1日1回制限 | 営業時間外メッセージは1日1回のみ | OutOfOffice |
| BR-82-05 | 初回のみ | グリーティングは顧客からの最初のメッセージ時のみ | Greeting |
| BR-82-06 | メッセージングウィンドウ | 自動解決メッセージはメッセージングウィンドウ内のみ | AutoResolve |
| BR-82-07 | WebWidget限定 | メール収集はWebWidgetインボックスのみ | EmailCollect |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| テンプレートメッセージ作成 | messages | INSERT | テンプレートメッセージレコード作成 |
| 今日のテンプレート確認 | messages | SELECT | 営業時間外1日1回チェック |
| メール収集済み確認 | messages | SELECT | content_type: input_email確認 |

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

#### messages

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | message_type | :template | 固定値 |
| INSERT | content | テンプレート本文 | インボックス設定から取得 |
| INSERT | content_type | input_csat / input_email / nil | テンプレート種別による |
| INSERT | account_id | 会話のaccount_id | 自動設定 |
| INSERT | inbox_id | 会話のinbox_id | 自動設定 |
| SELECT | message_type, created_at | template, today | 1日1回チェック用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StandardError | メッセージ作成失敗 | ChatwootExceptionTrackerで記録、処理は継続 |

### リトライ仕様

テンプレートメッセージ送信失敗時はリトライせず、例外を記録して処理を継続

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

各テンプレートメッセージ作成はActiveRecord::Base.transactionでラップ

## パフォーマンス要件

- メッセージ受信ごとに同期実行されるため、高速処理が必要
- 不要なDB問い合わせを条件判定で回避

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

- テンプレートメッセージはシステムが自動生成するため、XSS対策は設定入力時に実施
- 顧客からの入力値は直接使用しない

## 備考

- prepend_mod_withによりEnterprise版で拡張可能
- CSAT送信はHookExecutionServiceではなく、別のトリガーで実行される

---

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

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

### 推奨読解順序

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

テンプレートメッセージはmessagesテーブルにmessage_type: templateとして保存される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | message.rb | `app/models/message.rb` | message_type enumの定義 |

**読解のコツ**: message_typeが:templateの場合、自動生成されたシステムメッセージであることを示す

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hook_execution_service.rb | `app/services/message_templates/hook_execution_service.rb` | テンプレート送信の起点 |

**主要処理フロー**:
1. **4-10行目**: performメソッドで前提条件チェック
2. **17-21行目**: trigger_templatesで各テンプレート送信
3. **23-32行目**: should_send_out_of_office_message?で営業時間外判定
4. **35-43行目**: should_send_greeting?でグリーティング判定
5. **51-53行目**: should_send_email_collect?でメール収集判定

#### Step 3: 各テンプレートクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | greeting.rb | `app/services/message_templates/template/greeting.rb` | グリーティングメッセージ生成 |
| 3-2 | out_of_office.rb | `app/services/message_templates/template/out_of_office.rb` | 営業時間外メッセージ生成 |
| 3-3 | email_collect.rb | `app/services/message_templates/template/email_collect.rb` | メール収集メッセージ生成 |
| 3-4 | csat_survey.rb | `app/services/message_templates/template/csat_survey.rb` | CSAT調査メッセージ生成 |
| 3-5 | auto_resolve.rb | `app/services/message_templates/template/auto_resolve.rb` | 自動解決メッセージ生成 |

**主要処理フロー（Greeting）**:
- **4-11行目**: performでトランザクション内メッセージ作成
- **18-27行目**: greeting_message_paramsでメッセージパラメータ生成

**主要処理フロー（OutOfOffice）**:
- **4-9行目**: perform_if_applicableクラスメソッドで条件付き実行
- **12-19行目**: performでメッセージ作成

**主要処理フロー（CsatSurvey）**:
- **14-18行目**: message_contentでカスタムメッセージまたはデフォルト
- **20-29行目**: csat_survey_message_paramsでcontent_type: input_csat設定

**主要処理フロー（AutoResolve）**:
- **7-11行目**: メッセージングウィンドウ内外で処理分岐
- **23-31行目**: ウィンドウ外の場合はアクティビティメッセージを作成

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

```
MessageTemplates::HookExecutionService.perform
    │
    ├─ should_send_out_of_office_message?
    │      └─ MessageTemplates::Template::OutOfOffice.new.perform
    │
    ├─ should_send_greeting?
    │      └─ MessageTemplates::Template::Greeting.new.perform
    │
    └─ should_send_email_collect?
           └─ MessageTemplates::Template::EmailCollect.new.perform

[別トリガー]
Conversation#resolved
    └─ MessageTemplates::Template::CsatSurvey.new.perform

[自動解決時]
AutoResolveJob
    └─ MessageTemplates::Template::AutoResolve.new.perform
```

### データフロー図

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

受信メッセージ ───▶ HookExecutionService ───▶ テンプレートメッセージ
                        │                           │
                        ├─ 条件判定                  ├─ messages INSERT
                        │                           │
                        └─ テンプレート選択           └─ WebSocket配信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hook_execution_service.rb | `app/services/message_templates/hook_execution_service.rb` | サービス | テンプレート送信の制御 |
| greeting.rb | `app/services/message_templates/template/greeting.rb` | サービス | グリーティングメッセージ |
| out_of_office.rb | `app/services/message_templates/template/out_of_office.rb` | サービス | 営業時間外メッセージ |
| email_collect.rb | `app/services/message_templates/template/email_collect.rb` | サービス | メール収集メッセージ |
| csat_survey.rb | `app/services/message_templates/template/csat_survey.rb` | サービス | CSAT調査メッセージ |
| auto_resolve.rb | `app/services/message_templates/template/auto_resolve.rb` | サービス | 自動解決メッセージ |
| inbox.rb | `app/models/inbox.rb` | モデル | テンプレート設定の保持 |
| message.rb | `app/models/message.rb` | モデル | メッセージデータモデル |
