# 機能設計書 36-チャットメッセージ追加

## 概要

本ドキュメントは、Etherpadのチャットメッセージ追加機能（appendChatMessage）の設計仕様を記載する。

### 本機能の処理概要

本機能は、指定されたパッドにチャットメッセージを追加するAPI機能である。メッセージはデータベースに保存されると同時に、接続中の全クライアントにリアルタイムで配信される。

**業務上の目的・背景**：Etherpadの共同編集環境において、チャット機能はユーザー間のコミュニケーションに不可欠である。この機能は、API経由でチャットメッセージを追加することで、ボットによる自動メッセージ送信や外部システムからの通知機能を実現する。また、通常のクライアントからのチャット送信もSocket.IO経由で同様の処理が行われる。

**機能の利用シーン**：パッド編集画面からのチャット送信、ボットによる自動通知、外部システムからのメッセージ送信、管理者からのアナウンス配信など。

**主要な処理内容**：
1. パッドIDと本文の検証
2. 時刻の設定（未指定の場合は現在時刻）
3. ChatMessageオブジェクトの生成
4. メッセージのデータベース保存
5. 接続中の全クライアントへの配信

**関連システム・外部連携**：REST API経由でHTTPリクエストを受け付ける。Socket.IO経由で接続中クライアントにメッセージを配信する。データベース（ueberDB2）にメッセージを保存する。

**権限による制御**：APIキー認証またはSSO認証により、API呼び出しの認可を行う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 主画面 | チャット入力欄からメッセージを送信 |

## 機能種別

データ追加（INSERT操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | 対象パッドのID | 文字列型であること |
| text | string | Yes | チャットメッセージ本文 | 文字列型であること |
| authorID | string | No | 送信者の著者ID | - |
| time | integer | No | 送信時刻（ミリ秒エポック） | 整数型であること（未指定時は現在時刻） |

### 入力データソース

REST API PATCHリクエストのボディとしてJSON形式でパラメータを受け取る。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | 結果コード（0: 成功） |
| message | string | 結果メッセージ（"ok"） |
| data | null | 正常終了時はnull |

### 出力先

- HTTPレスポンスとしてJSON形式で返却
- Socket.IO経由で接続中クライアントにCHAT_MESSAGEイベントとして配信

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ PATCH /api/2/chats/messages
2. 認証・認可チェック
   └─ APIキーまたはSSOトークンの検証
3. パラメータ検証
   └─ textが文字列であることを確認
4. 時刻設定
   └─ timeが未指定または整数でない場合は現在時刻を使用
5. ChatMessageオブジェクト生成
   └─ new ChatMessage(text, authorID, time)
6. メッセージ保存・配信
   └─ sendChatMessageToPadClients()を呼び出し
       ├─ pad.appendChatMessage()でDB保存
       └─ socketio.emit()でクライアント配信
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D{text検証}
    D -->|文字列でない| E[text is not a string]
    D -->|正常| F{time検証}
    F -->|未指定/非整数| G[現在時刻を設定]
    F -->|整数| H[指定時刻を使用]
    G --> I[ChatMessage生成]
    H --> I
    I --> J[sendChatMessageToPadClients]
    J --> K[DB保存]
    K --> L[クライアント配信]
    L --> M[JSONレスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-01 | テキスト必須 | text パラメータは文字列型で必須 | 常時 |
| BR-36-02 | 時刻自動設定 | timeが未指定または非整数の場合は現在時刻を使用 | time未指定時 |
| BR-36-03 | リアルタイム配信 | メッセージは即座に接続中クライアントに配信される | 常時 |
| BR-36-04 | chatHead更新 | メッセージ追加時にchatHeadがインクリメントされる | 常時 |
| BR-36-05 | 著者名付加 | 配信時にauthorIDから著者名を取得して付加 | 常時 |

### 計算ロジック

chatHeadはメッセージ追加時に自動的にインクリメントされる。新しいメッセージは pad:{padID}:chat:{chatHead} というキーでデータベースに保存される。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メッセージ保存 | pad:{padID}:chat:{n} | INSERT | チャットメッセージを保存 |
| パッド更新 | pad:{padID} | UPDATE | chatHeadの更新 |

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

#### pad:{padID}:chat:{n}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | text, authorId, time | メッセージデータ | displayNameは保存しない |

#### pad:{padID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | chatHead | インクリメント | saveToDatabase()で保存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | apierror | textが文字列でない | 文字列型のtextを指定 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを使用 |

### リトライ仕様

リトライは非推奨（メッセージの重複を避けるため）

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

メッセージ保存とchatHead更新は並列で行われる（Promise.all）。いずれかが失敗した場合でも部分的に処理が完了する可能性がある。

## パフォーマンス要件

- レスポンス時間: 200ms以内（通常条件下）
- Socket.IO配信はレスポンス返却と並行して行われる

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

- APIキー認証またはSSO認証が必須
- authorIDはクライアントからの入力を信頼するため、なりすましの可能性あり（API利用時）
- Socket.IO経由の場合はセッション情報からauthorIDを取得するためセキュア

## 備考

API Version 1.2.12で追加された機能。Socket.IO経由のCHAT_MESSAGEイベントでも同様の機能が提供される。API経由の場合、getPadSafe()の呼び出しがないため、存在しないパッドにもメッセージを送信できる可能性がある（要注意）。

---

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

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

### 推奨読解順序

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

まず、ChatMessageクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ChatMessage.ts | `src/static/js/ChatMessage.ts` | ChatMessageクラスの定義とプロパティ |

**読解のコツ**: ChatMessageはtext, authorId, time, displayNameを持つデータクラス。

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

REST APIのエンドポイント定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | PATCH /chats/messagesのマッピング定義（1175-1204行目） |

**主要処理フロー**:
1. **1175-1204行目**: エンドポイント定義とappendChatMessage関数へのマッピング

#### Step 3: API関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | appendChatMessage関数の実装（379-394行目） |

**主要処理フロー**:
- **381-383行目**: textの検証
- **386-388行目**: timeのデフォルト設定
- **393行目**: sendChatMessageToPadClients呼び出し

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | sendChatMessageToPadClients()関数（512-526行目） |
| 4-2 | Pad.ts | `src/node/db/Pad.ts` | appendChatMessage()メソッド（334-345行目） |

**主要処理フロー**:
- **512-526行目**: フック呼び出し、DB保存、クライアント配信
- **334-345行目**: chatHeadインクリメントとメッセージ保存

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

```
RestAPI.expressCreateServer
    │
    ├─ app.use('/api/2', ...)
    │      └─ apiHandler.handle('1.2.12', 'appendChatMessage', fields)
    │              └─ API.appendChatMessage(padID, text, authorID, time)
    │                      ├─ new ChatMessage(text, authorID, time)
    │                      └─ padMessageHandler.sendChatMessageToPadClients()
    │                              ├─ padManager.getPad(padId)
    │                              ├─ hooks.aCallAll('chatNewMessage')
    │                              ├─ pad.appendChatMessage(message)
    │                              │       ├─ this.chatHead++
    │                              │       ├─ db.set(`pad:${id}:chat:${chatHead}`)
    │                              │       └─ saveToDatabase()
    │                              ├─ authorManager.getAuthorName()
    │                              └─ socketio.emit('CHAT_MESSAGE')
    │
    └─ res.json(response)
```

### データフロー図

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

padID, text, authorID, time ──▶ ChatMessage生成
                                       │
                                       ▼
                          sendChatMessageToPadClients()
                                       │
                    ┌──────────────────┼──────────────────┐
                    ▼                  ▼                  ▼
             pad.appendChatMessage   getAuthorName   chatNewMessage hook
                    │                  │
                    ▼                  ▼
             DB保存 + chatHead++   displayName取得
                                       │
                                       ▼
                               socketio.emit()
                                       │
                                       ▼
                               CHAT_MESSAGE ────────────▶ 接続クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | appendChatMessage関数の実装 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | sendChatMessageToPadClients関数 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | appendChatMessageメソッド |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | REST APIエンドポイント定義 |
| ChatMessage.ts | `src/static/js/ChatMessage.ts` | ソース | ChatMessageクラスの定義 |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者名取得処理 |
