# 機能設計書 128-メール

## 概要

本ドキュメントは、Chatwootのメールチャネル連携機能の設計仕様を記載する。この機能は、IMAP/SMTP経由でメールを送受信し、Chatwootの会話として管理するためのチャネル統合機能である。

### 本機能の処理概要

メール連携は、IMAP/SMTPプロトコルまたはMicrosoft/Google認可を使用してメールの送受信を行い、Chatwootの会話として管理する。受信メールは自動的に連絡先・会話に変換され、返信メールはChatwootから直接送信できる。

**業務上の目的・背景**：メールは依然として主要なビジネスコミュニケーション手段であり、多くの顧客がメール経由でサポートを求める。本機能により、メール対応をChatwootの統合インターフェースで管理でき、他チャネルと合わせたオムニチャネルサポート体制を構築できる。

**機能の利用シーン**：
- 顧客からのメール問い合わせを受信する場面
- エージェントがChatwootからメールで返信する場面
- サポートアドレス（support@example.com等）への問い合わせを一元管理する場面
- Microsoft 365やGoogleワークスペースのメールを連携する場面

**主要な処理内容**：
1. IMAP/SMTPまたはMicrosoft/Google認可での接続設定
2. 定期的なIMAP受信処理（メールフェッチ）
3. SMTPまたはAPI経由でのメール送信
4. 転送アドレスを使用したメール受信
5. 連絡先（メール送信者）の自動作成・更新

**関連システム・外部連携**：
- IMAP/SMTPサーバー
- Microsoft Graph API（Microsoft認可時）
- Google Gmail API（Google認可時）

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

## 関連画面

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

## 機能種別

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

## 入力仕様

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| email | String | Yes | メールアドレス | 一意性 |
| forward_to_email | String | Auto | 転送先アドレス | 一意性、自動生成 |
| imap_enabled | Boolean | No | IMAP有効化 | デフォルト: false |
| imap_address | String | Cond | IMAPサーバーアドレス | IMAP有効時必須 |
| imap_port | Integer | Cond | IMAPポート | IMAP有効時必須 |
| imap_login | String | Cond | IMAPログイン | IMAP有効時必須 |
| imap_password | String | Cond | IMAPパスワード | IMAP有効時必須 |
| imap_enable_ssl | Boolean | No | IMAP SSL有効化 | デフォルト: true |
| smtp_enabled | Boolean | No | SMTP有効化 | デフォルト: false |
| smtp_address | String | Cond | SMTPサーバーアドレス | SMTP有効時必須 |
| smtp_port | Integer | Cond | SMTPポート | SMTP有効時必須 |
| smtp_login | String | Cond | SMTPログイン | SMTP有効時必須 |
| smtp_password | String | Cond | SMTPパスワード | SMTP有効時必須 |
| provider | String | No | プロバイダ（microsoft/google） | - |
| verified_for_sending | Boolean | No | 送信検証済みフラグ | デフォルト: false |

### 入力データソース

- 設定フォームからの入力
- Microsoft/Google OAuth認可フローからトークン取得
- Chatwoot会話画面からのメール返信

## 出力仕様

### 出力データ（メール受信時）

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

### 出力データ（メール送信時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message_id | String | メールのMessage-ID（source_idとして保存） |

### 出力先

- Chatwootデータベース（messages, conversations, contacts）
- メールサーバー（送信メール）

## 処理フロー

### 処理シーケンス（メール受信 - IMAP）

```
1. 定期ジョブでIMAPフェッチ実行
   └─ Imap::FetchEmailService / GoogleFetchEmailService / MicrosoftFetchEmailService
2. メールボックスから新規メール取得
   └─ SINCEで前日以降を検索
   └─ 既存メッセージ（source_id重複）をスキップ
3. メール解析・メッセージ作成
   └─ 連絡先の作成または取得
   └─ 会話の作成または取得
   └─ メッセージの作成
```

### 処理シーケンス（メール受信 - 転送）

```
1. 転送メールをforward_to_emailで受信
   └─ Mailgun等のインバウンドメール処理
2. MailboxController等で処理
   └─ 連絡先・会話・メッセージの作成
```

### 処理シーケンス（メール送信）

```
1. Chatwootでメッセージ作成
   └─ email_notifiable_message?判定
2. SendOnEmailServiceの実行
   └─ ConversationReplyMailer#email_reply呼び出し
3. SMTP/API経由でメール送信
   └─ message_idをsource_idとして保存
```

### フローチャート

```mermaid
flowchart TD
    A[メッセージ作成] --> B{email_notifiable_message?}
    B -->|No| C[処理終了]
    B -->|Yes| D[ConversationReplyMailer]
    D --> E[email_reply]
    E --> F[deliver_now]
    F --> G{送信成功?}
    G -->|Yes| H[source_id保存]
    G -->|No| I[failed & エラーメッセージ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 転送アドレス自動生成 | forward_to_emailは{SecureRandom.hex}@{inbound_email_domain}形式で自動生成 | チャネル作成時 |
| BR-002 | 重複メール防止 | Message-ID（source_id）で重複チェック | メール受信時 |
| BR-003 | IMAPフェッチ範囲 | 前日以降のメールを対象 | IMAPフェッチ時 |
| BR-004 | メール通知対象 | email_notifiable_message?がtrueのメッセージのみ送信 | メール送信時 |

### 計算ロジック

- forward_to_email生成: `#{SecureRandom.hex}@#{account.inbound_email_domain}`

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

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

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

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

#### channel_email

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | email, forward_to_email, imap_*, smtp_*, provider, account_id | 設定フォームから取得 | imap_password, smtp_password暗号化 |
| SELECT | imap_*, smtp_* | チャネルID指定 | メール送受信時に使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| authentication_error | Auth Error | IMAP/SMTP認証失敗 | 認証情報を確認 |
| connection_error | Network Error | サーバー接続失敗 | サーバー設定を確認 |
| send_failure | SMTP Error | メール送信失敗 | failedステータスに更新 |

### リトライ仕様

- AUTHORIZATION_ERROR_THRESHOLD = 10（10回の認可エラーで再認可要求）

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

- メール受信時：メッセージ・会話・連絡先の作成は単一トランザクション
- メール送信時：deliver_now後にsource_id更新

## パフォーマンス要件

- IMAPフェッチは定期ジョブで実行
- バッチ処理（10件単位）でMessage-IDを取得

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

- imap_password、smtp_passwordは暗号化して保存
- forward_to_emailは推測困難な形式で自動生成
- Microsoft/Googleプロバイダ使用時はOAuth認証

## 備考

- Reauthorizableモジュールで認可エラー時の再認可フロー対応
- AUTHORIZATION_ERROR_THRESHOLD = 10
- microsoft?、google?、legacy_google?メソッドでプロバイダ判定

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | email.rb | `app/models/channel/email.rb` | チャネルモデルの構造、プロバイダ判定メソッド |

**読解のコツ**:
- `provider`でmicrosoft/googleを判定
- `forward_to_email`は自動生成される

**主要処理フロー**:
- **37-40行目**: スキーマ構造とReauthorizable include
- **41行目**: AUTHORIZATION_ERROR_THRESHOLD = 10
- **44-47行目**: 暗号化設定（imap_password, smtp_password）
- **49行目**: テーブル名定義
- **50-52行目**: EDITABLE_ATTRS定義
- **54-55行目**: emailとforward_to_emailの一意性バリデーション
- **57行目**: ensure_forward_to_email before_validation
- **63-68行目**: microsoft?、google?判定メソッド
- **76-78行目**: ensure_forward_to_email実装

#### Step 2: メール送信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | send_on_email_service.rb | `app/services/email/send_on_email_service.rb` | メール送信処理 |

**主要処理フロー（SendOnEmailService）**:
- **8-17行目**: perform_reply - email_notifiable_message?判定後にメール送信
- **11行目**: ConversationReplyMailer.email_reply.deliver_now
- **13行目**: source_idをmessage_idで更新

#### Step 3: メール受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | base_fetch_email_service.rb | `app/services/imap/base_fetch_email_service.rb` | IMAP受信基底クラス |
| 3-2 | fetch_email_service.rb | `app/services/imap/fetch_email_service.rb` | 標準IMAP受信 |
| 3-3 | google_fetch_email_service.rb | `app/services/imap/google_fetch_email_service.rb` | Google IMAP受信 |
| 3-4 | microsoft_fetch_email_service.rb | `app/services/imap/microsoft_fetch_email_service.rb` | Microsoft IMAP受信 |

**主要処理フロー（BaseFetchEmailService）**:
- **10-14行目**: perform - メールフェッチ後にIMAP接続終了
- **42-47行目**: fetch_mail_for_channel - メッセージIDリストを取得して処理
- **49-70行目**: process_message_id - 重複チェック後にメール内容取得
- **74-99行目**: fetch_message_ids_with_sequence - バッチ処理でメッセージID取得
- **104-106行目**: fetch_available_mail_sequence_numbers - SINCE検索で前日以降取得
- **108-113行目**: build_imap_client - IMAP接続構築

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

```
[メール送信フロー]
Message#after_create_commit
    │
    └─ SendReplyJob.perform_later
           │
           └─ Email::SendOnEmailService#perform
                  │
                  ├─ email_notifiable_message? チェック
                  │
                  └─ ConversationReplyMailer
                         │
                         └─ email_reply.deliver_now
                                │
                                └─ SMTP/API送信

[メール受信フロー（IMAP）]
定期ジョブ
    │
    └─ Imap::BaseFetchEmailService#perform
           │
           ├─ fetch_emails
           │      │
           │      └─ fetch_mail_for_channel
           │             │
           │             ├─ fetch_message_ids_with_sequence
           │             │      └─ imap_client.search(['SINCE', since])
           │             │
           │             └─ process_message_id
           │                    │
           │                    ├─ 重複チェック
           │                    └─ メール内容取得
           │
           └─ terminate_imap_connection
```

### データフロー図

```
[送信フロー]
[Chatwoot]              [処理]                    [メールサーバー]

Message作成     ───▶ SendReplyJob
                      │
                      ▼
                SendOnEmailService
                      │
                      ├─▶ email_notifiable_message?
                      │
                      └─▶ ConversationReplyMailer
                             │
                             └─▶ email_reply.deliver_now
                                    │
                                    ▼
                              SMTP/API送信  ───▶ メールサーバー
                                    │
                                    ▼
                              message_id受信
                                    │
                                    ▼
                              source_id保存

[受信フロー（IMAP）]
[メールサーバー]        [処理]                    [Chatwoot]

IMAPメールボックス ◀─── imap_client.search
                      │
                      ▼
                fetch_message_ids_with_sequence
                      │
                      ▼
                process_message_id
                      │
                      ├─▶ 重複チェック
                      │
                      └─▶ メール内容取得
                             │
                             └─▶ Message/Contact/Conversation作成
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| email.rb | `app/models/channel/email.rb` | モデル | チャネル定義 |
| send_on_email_service.rb | `app/services/email/send_on_email_service.rb` | サービス | メール送信処理 |
| base_fetch_email_service.rb | `app/services/imap/base_fetch_email_service.rb` | サービス | IMAP受信基底クラス |
| fetch_email_service.rb | `app/services/imap/fetch_email_service.rb` | サービス | 標準IMAP受信 |
| google_fetch_email_service.rb | `app/services/imap/google_fetch_email_service.rb` | サービス | Google IMAP受信 |
| microsoft_fetch_email_service.rb | `app/services/imap/microsoft_fetch_email_service.rb` | サービス | Microsoft IMAP受信 |
| conversation_reply_mailer.rb | `app/mailers/conversation_reply_mailer.rb` | メーラー | 返信メール送信 |
| google/authorizations_controller.rb | `app/controllers/api/v1/accounts/google/authorizations_controller.rb` | コントローラー | Google認可 |
| microsoft/authorizations_controller.rb | `app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb` | コントローラー | Microsoft認可 |
