# 機能設計書 130-LINE

## 概要

本ドキュメントは、ChatwootのLINE Official Account連携機能の設計仕様を記載する。この機能は、LINE Messaging APIを通じたメッセージングをChatwootで一元管理するためのチャネル統合機能である。

### 本機能の処理概要

LINE連携は、LINE Messaging APIを通じてLINE Official Accountに送信されたメッセージを受信し、Chatwootの会話として管理する。エージェントはChatwootからLINEユーザーへの返信を直接送信できる。

**業務上の目的・背景**：LINEは日本・台湾・タイ・インドネシアなどで圧倒的なシェアを持つメッセージングプラットフォームであり、これらの地域でのカスタマーサポートにおいて必須のチャネルとなっている。本機能により、LINE経由の顧客対応をChatwootで統合管理でき、アジア市場でのカスタマーサポート体制を強化できる。

**機能の利用シーン**：
- 顧客からのLINEメッセージを受信・返信する場面
- 画像・動画・音声・ファイルの送受信
- リッチメニュー・Flexメッセージと連携した対話
- ステッカーの受信（マークダウン画像として表示）

**主要な処理内容**：
1. LINE Official Accountの登録とWebhook設定
2. Webhookを通じたメッセージ・ステッカーの受信処理
3. LINE Messaging APIを使用したメッセージ・添付ファイル送信
4. Flexメッセージ（選択肢付きメッセージ）の送信
5. 連絡先（LINEユーザー）の自動作成・プロフィール取得

**関連システム・外部連携**：
- LINE Messaging API
- LINE Webhook

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

## 関連画面

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

## 機能種別

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

## 入力仕様

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| line_channel_id | String | Yes | LINEチャネルID | 一意性 |
| line_channel_secret | String | Yes | チャネルシークレット | 必須 |
| line_channel_token | String | Yes | チャネルアクセストークン | 必須 |
| account_id | Integer | Yes | Chatwootアカウント | 必須 |

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content | String | No | メッセージ本文 | - |
| content_type | String | No | コンテンツタイプ（text/input_select） | - |
| attachments | Array | No | 添付ファイル | image/videoのみ |

### 入力データソース

- LINE Developers Consoleからのチャネル情報取得
- Chatwoot会話画面からのメッセージ入力

## 出力仕様

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

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

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

| 項目名 | 型 | 説明 |
|--------|-----|------|
| status | String | delivered/failed |

### 出力先

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

## 処理フロー

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

```
1. LINE Webhookからイベント受信
   └─ LineController#process_payloadで処理
2. 署名検証
   └─ x-line-signatureヘッダーで検証
3. LineEventsJobで処理
   └─ IncomingMessageService実行
4. イベント処理
   └─ message/stickerイベントを処理
   └─ get_line_contact_infoでプロフィール取得
   └─ 連絡先・会話・メッセージの作成
```

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

```
1. Chatwootでメッセージ作成
   └─ Message#after_create_commitコールバック
2. SendOnLineServiceの実行
   └─ build_payload実行
3. LINE Messaging APIへ送信
   └─ client.push_message呼び出し
4. レスポンス処理
   └─ 200: deliveredステータス
   └─ その他: failedステータス、external_error設定
```

### フローチャート

```mermaid
flowchart TD
    A[Webhook受信] --> B{署名検証}
    B -->|失敗| C[処理終了]
    B -->|成功| D{イベントタイプ}
    D -->|message/sticker| E[get_line_contact_info]
    D -->|その他| C
    E --> F{userIdあり?}
    F -->|No| C
    F -->|Yes| G[連絡先作成/取得]
    G --> H[会話作成/取得]
    H --> I[メッセージ作成]
    I --> J{添付ファイルあり?}
    J -->|Yes| K[添付ファイル処理]
    J -->|No| L[メッセージ保存]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 署名検証 | x-line-signatureヘッダーで検証 | Webhook受信時 |
| BR-002 | ステッカー表示 | ステッカーはマークダウン画像形式で表示 | ステッカー受信時 |
| BR-003 | 添付ファイル制限 | image/videoのみサポート | メッセージ送信時 |
| BR-004 | Flexメッセージ | input_selectの場合はFlexメッセージ（ボタン）を生成 | メッセージ送信時 |
| BR-005 | プッシュ送信 | push_messageで送信（reply不使用） | メッセージ送信時 |

### 計算ロジック

- ステッカー画像URL: `https://stickershop.line-scdn.net/stickershop/v1/sticker/#{sticker_id}/android/sticker.png`

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

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

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

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

#### channel_line

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | line_channel_id, line_channel_secret, line_channel_token, account_id | 設定フォームから取得 | secret, token暗号化 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| invalid_signature | Auth Error | 署名検証失敗 | チャネルシークレットを確認 |
| send_failed | API Error | メッセージ送信失敗 | external_errorに詳細保存 |
| user_not_found | API Error | ユーザーIDなし | 処理をスキップ |

### リトライ仕様

- 自動リトライなし
- エラー詳細をexternal_errorに保存

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

- メッセージ受信時：メッセージ・会話・連絡先の作成は単一トランザクション

## パフォーマンス要件

- Webhook処理はバックグラウンドジョブで実行

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

- line_channel_secret、line_channel_tokenは暗号化して保存
- Webhook署名検証で不正リクエスト防止
- 開発環境ではSSL検証をスキップ（VERIFY_NONE）

## 備考

- EDITABLE_ATTRS = [:line_channel_id, :line_channel_secret, :line_channel_token]
- ステッカーは画像URLをマークダウン形式で表示（著作権考慮）
- Line::Bot::Client gemを使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | line.rb | `app/models/channel/line.rb` | チャネルモデルの構造、Line::Bot::Client生成 |

**読解のコツ**:
- `Line::Bot::Client`をclientメソッドで生成
- 開発環境ではSSL検証をスキップ

**主要処理フロー**:
- **18行目**: Channel::Line定義
- **22-25行目**: 暗号化設定（line_channel_secret, line_channel_token）
- **27行目**: テーブル名定義
- **28行目**: EDITABLE_ATTRS定義
- **30-32行目**: バリデーション
- **38-46行目**: clientメソッド - Line::Bot::Client生成

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | send_on_line_service.rb | `app/services/line/send_on_line_service.rb` | 送信処理、Flexメッセージ生成 |

**主要処理フロー（SendOnLineService）**:
- **8-22行目**: perform_reply - push_messageでメッセージ送信
- **24-30行目**: build_payload - input_select判定
- **32-40行目**: build_text_payload - テキスト・添付ファイルペイロード
- **42-53行目**: attachments - image/videoのみ対応
- **55-61行目**: text_message - テキストメッセージ構造
- **63-84行目**: build_input_select_payload - Flexメッセージ構造
- **86-99行目**: input_select_to_button - ボタン配列生成
- **101-112行目**: external_error - エラーメッセージ抽出

#### Step 3: メッセージ受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | line_controller.rb | `app/controllers/webhooks/line_controller.rb` | Webhook受信 |
| 3-2 | incoming_message_service.rb | `app/services/line/incoming_message_service.rb` | メッセージ処理 |

**主要処理フロー（IncomingMessageService）**:
- **9-14行目**: perform - イベントがなければ終了
- **18-32行目**: parse_events - 各イベントを処理
- **35-46行目**: message_created? - メッセージビルド
- **48-57行目**: message_content - text/sticker判定
- **65-67行目**: sticker_image_url - マークダウン画像形式
- **75-96行目**: attach_files - 添付ファイル処理
- **106-108行目**: event_type_message? - message/stickerイベント判定
- **123-125行目**: get_line_contact_info - プロフィール取得
- **154-159行目**: contact_attributes - 連絡先属性

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

```
[メッセージ送信フロー]
Message#after_create_commit
    │
    └─ SendReplyJob.perform_later
           │
           └─ Line::SendOnLineService#perform
                  │
                  ├─ build_payload
                  │      │
                  │      ├─ [input_select] build_input_select_payload
                  │      │      └─ input_select_to_button
                  │      │
                  │      └─ [その他] build_text_payload
                  │             ├─ text_message
                  │             └─ attachments
                  │
                  └─ channel.client.push_message
                         │
                         └─ LINE Messaging API

[メッセージ受信フロー]
LINE Webhook
    │
    └─ Webhooks::LineController#process_payload
           │
           └─ LineEventsJob.perform_later
                  │
                  └─ Line::IncomingMessageService#perform
                         │
                         └─ parse_events
                                │
                                ├─ event_type_message? チェック
                                ├─ get_line_contact_info
                                │      └─ client.get_profile
                                ├─ set_contact
                                ├─ set_conversation
                                └─ message_created?
                                       └─ attach_files
```

### データフロー図

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

Message作成     ───▶ SendReplyJob
                      │
                      ▼
                SendOnLineService
                      │
                      ├─▶ build_payload
                      │      │
                      │      ├─▶ [text] text_message
                      │      ├─▶ [input_select] Flex Message
                      │      └─▶ [attachments] image/video
                      │
                      └─▶ channel.client.push_message
                             │
                             ▼
                      LINE Messaging API ───▶ LINE
                             │
                             ▼
                      レスポンス受信     ◀───  API Response
                             │
                             ├─▶ [200] delivered
                             └─▶ [その他] failed + external_error

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

Webhook Event   ───▶ LineController
                      │
                      ├─▶ 署名検証
                      │
                      └─▶ LineEventsJob
                             │
                             └─▶ IncomingMessageService
                                    │
                                    ├─▶ get_line_contact_info
                                    │      └─ プロフィール取得
                                    │
                                    ├─▶ set_contact
                                    │      └─ displayName, pictureUrl
                                    │
                                    ├─▶ set_conversation
                                    │
                                    └─▶ message_created?
                                           │
                                           ├─▶ [text] テキスト保存
                                           ├─▶ [sticker] マークダウン画像
                                           └─▶ [media] 添付ファイル取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| line.rb | `app/models/channel/line.rb` | モデル | チャネル定義・クライアント生成 |
| send_on_line_service.rb | `app/services/line/send_on_line_service.rb` | サービス | メッセージ送信処理 |
| incoming_message_service.rb | `app/services/line/incoming_message_service.rb` | サービス | メッセージ受信処理 |
| line_controller.rb | `app/controllers/webhooks/line_controller.rb` | コントローラー | Webhook受信 |
| line_events_job.rb | `app/jobs/webhooks/line_events_job.rb` | ジョブ | Webhook処理 |
