# 機能設計書 63-Slack通知

## 概要

本ドキュメントは、Ghost CMSにおけるSlack通知機能の設計を記述する。この機能は、記事の公開時やマイルストーン達成時にSlackへ通知を送信する機能である。

### 本機能の処理概要

この機能では、設定されたSlack Webhook URLに対して、記事公開などのイベント発生時に自動的に通知メッセージを送信する。

**業務上の目的・背景**：サイト運営チームがSlackでコミュニケーションを行っている場合、新記事公開やマイルストーン達成などの重要イベントをリアルタイムで共有することで、チームの連携を強化できる。

**機能の利用シーン**：
- 新しい記事が公開された際のチーム通知
- マイルストーン（会員数・ARR）達成時の祝福通知
- Slack連携のテスト通知送信

**主要な処理内容**：
1. 記事公開イベントの監視（post.published）
2. Slack Webhook URLへのHTTPリクエスト送信
3. マイルストーン達成イベントの通知
4. テスト通知の送信

**関連システム・外部連携**：Slack Incoming Webhooks API

**権限による制御**：Slack URLの設定は管理者のみ可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 設定画面（インテグレーション） | 参照画面 | Slack Webhook URLの設定 |

## 機能種別

外部連携 / イベント通知

## 入力仕様

### 入力パラメータ（記事公開通知）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| model | Object | Yes | 公開された記事モデル | - |
| options.importing | boolean | No | インポート中フラグ | - |

### 入力パラメータ（マイルストーン通知）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| milestone | Object | Yes | マイルストーンオブジェクト | - |
| milestone.type | string | Yes | 'arr' または 'members' | - |
| milestone.value | number | Yes | 達成値 | - |
| milestone.currency | string | No | 通貨（ARRの場合） | - |
| meta.reason | string | No | 'import'/'email'/'skipped'/'initial' | - |
| meta.currentValue | number | No | 現在の値 | - |

### 入力データソース

- Settings テーブル（slack_url, slack_username）
- DomainEvents（MilestoneCreatedEvent）
- Model Events（post.published）

## 出力仕様

### 出力データ（Slackペイロード）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| text | string | メッセージテキスト |
| unfurl_links | boolean | リンクプレビュー有効化 |
| icon_url | string | アイコンURL（サイトアイコン） |
| username | string | 表示ユーザー名 |
| attachments | array | リッチメッセージ添付 |

### 出力先

- Slack Webhook URL（HTTP POST）

## 処理フロー

### 処理シーケンス

```
1. イベント受信
   └─ post.published または slack.test イベントを受信

2. 設定確認
   ├─ slack_url が設定されているか確認
   └─ 設定がなければ処理終了

3. 送信判定
   ├─ ページ（type=page）は送信しない
   ├─ デフォルト投稿（welcome等）は送信しない
   └─ インポート中（options.importing）は送信しない

4. ペイロード構築
   ├─ 記事URLの取得
   ├─ タイトル・説明・著者情報の取得
   └─ Slackメッセージ形式に変換

5. HTTP送信
   └─ Webhook URLへPOSTリクエスト
```

### フローチャート

```mermaid
flowchart TD
    A[post.published イベント] --> B{importing?}
    B -->|Yes| C[処理終了]
    B -->|No| D{slack_url設定あり?}
    D -->|No| C
    D -->|Yes| E{type=page?}
    E -->|Yes| C
    E -->|No| F{デフォルト投稿?}
    F -->|Yes| C
    F -->|No| G[ペイロード構築]
    G --> H[Slack HTTP POST]
    H --> I{成功?}
    I -->|Yes| J[処理完了]
    I -->|No| K[エラーログ出力]
    K --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ページ除外 | 固定ページ（type=page）は通知しない | post.published時 |
| BR-02 | デフォルト投稿除外 | welcome, the-editor等のデフォルト投稿は除外 | post.published時 |
| BR-03 | インポート除外 | データインポート時は通知しない | options.importing=true |
| BR-04 | ユーザー名デフォルト | slack_usernameが未設定の場合は'Ghost'を使用 | 通知送信時 |
| BR-05 | マイルストーン通知条件 | skipped/initialの場合は通知しない | MilestoneCreatedEvent時 |
| BR-06 | 最小閾値チェック | minThreshold以下のマイルストーンは通知しない | MilestoneCreatedEvent時 |

### 計算ロジック

記事説明文の抽出: HTMLから最初の3文を抽出（メンバー専用コンテンツは除外）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定取得 | settings | SELECT | slack_url, slack_username取得 |

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

#### settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | key='slack_url' | Webhook URL | settingsCacheを経由 |
| SELECT | key='slack_username' | 表示名 | settingsCacheを経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| URL_MISSING_INVALID | InternalServerError | URLが空または無効 | エラーログ出力 |
| - | InternalServerError | HTTP送信失敗 | エラーログ出力して処理継続 |

### リトライ仕様

テスト環境ではretry=0でリトライなし。本番環境ではgotライブラリのデフォルトリトライを使用。

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

外部APIへのHTTPリクエストのため、トランザクション制御は適用されない

## パフォーマンス要件

- 非同期処理によりメインフローをブロックしない
- タイムアウトは設定なし（gotライブラリのデフォルト）

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

- Webhook URLは設定画面でのみ確認可能
- HTTPSへのアップグレードはSlack側で実施
- User-Agentヘッダにバージョン情報を含める

## 備考

- 2つの独立したSlack通知システムが存在
  - slack.js: 記事公開通知（レガシー）
  - slack-notifications/: マイルストーン通知（新システム）

---

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

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

### 推奨読解順序

#### Step 1: レガシーSlack通知を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | slack.js | `ghost/core/core/server/services/slack.js` | 記事公開通知の実装 |

**読解のコツ**: イベントリスナーパターン（events.on）を理解する。ping関数がメインの処理ロジック。

**主要処理フロー**:
1. **29-37行目**: getSlackSettings - 設定取得
2. **48-171行目**: ping - メイン通知ロジック
3. **92-97行目**: ページ・デフォルト投稿の除外判定
4. **108-156行目**: Slackペイロード構築
5. **158-170行目**: HTTP POST送信
6. **173-184行目**: slackListener - イベントハンドラ
7. **192-200行目**: listen - イベント登録

#### Step 2: マイルストーンSlack通知を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | service.js | `ghost/core/core/server/services/slack-notifications/service.js` | サービスラッパー |
| 2-2 | slack-notifications-service.js | `ghost/core/core/server/services/slack-notifications/slack-notifications-service.js` | イベント購読とハンドラ |
| 2-3 | slack-notifications.js | `ghost/core/core/server/services/slack-notifications/slack-notifications.js` | 通知メッセージ構築 |

**主要処理フロー（slack-notifications-service.js）**:
- **69-83行目**: handleEvent - イベント処理
- **85-89行目**: subscribeEvents - MilestoneCreatedEvent購読

**主要処理フロー（slack-notifications.js）**:
- **57-148行目**: notifyMilestoneReceived - マイルストーン通知構築
- **157-180行目**: send - HTTP送信

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

```
[記事公開通知]
slack.js
    │
    ├─ listen()
    │      └─ events.on('post.published', slackListener)
    │
    └─ slackListener()
           └─ ping()
                  └─ request() → Slack Webhook

[マイルストーン通知]
slack-notifications/service.js
    │
    └─ SlackNotificationsServiceWrapper.init()
           │
           └─ SlackNotificationsService
                  │
                  └─ subscribeEvents()
                         │
                         └─ DomainEvents.subscribe(MilestoneCreatedEvent)
                                │
                                └─ handleEvent()
                                       │
                                       └─ SlackNotifications.notifyMilestoneReceived()
                                              │
                                              └─ send() → got.post()
```

### データフロー図

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

post.published ────▶ slackListener() ────▶ Slack Webhook
イベント                     │                  (HTTP POST)
                            ▼
                     ping()
                     - URL/タイトル取得
                     - ペイロード構築

MilestoneCreated ──▶ handleEvent() ──────▶ Slack Webhook
Event                       │                  (HTTP POST)
                           ▼
                    notifyMilestoneReceived()
                    - ブロック形式で構築
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| slack.js | `ghost/core/core/server/services/slack.js` | ソース | 記事公開通知 |
| service.js | `ghost/core/core/server/services/slack-notifications/service.js` | ソース | マイルストーン通知ラッパー |
| slack-notifications-service.js | `ghost/core/core/server/services/slack-notifications/slack-notifications-service.js` | ソース | イベント購読 |
| slack-notifications.js | `ghost/core/core/server/services/slack-notifications/slack-notifications.js` | ソース | 通知構築・送信 |
| index.js | `ghost/core/core/server/services/slack-notifications/index.js` | ソース | エクスポート |
