Cloud Run関数とCloud Schedulerでグラフ付きのSlack通知を自動化する

BigQueryのデータセットから過去7日間のセッション数を集計し、それを折れ線グラフに可視化してSlackに通知する仕組みを構築しました。Cloud Run関数を使用して集計とグラフ生成を行い、Cloud Schedulerで定期実行をスケジュールします。

## 実装

### Cloud Run関数
まず、Cloud Run関数を作成します。この関数は、BigQueryからデータを取得し、Matplotlibでグラフを作成した後、Slack APIを使用して通知を送信します。

`SLACK_API_TOKEN`と`SLACK_CHANNEL_ID`はランタイム環境変数に設定する必要がありますが、この時点ではまだ空で大丈夫です(次のステップで設定します)。

```main.py
import os
import matplotlib.pyplot as plt
from google.cloud import bigquery
from datetime import datetime, timedelta
import io
import pytz
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError


def create_weekly_total_sessions_chart(_):
    SLACK_TOKEN = os.environ.get('SLACK_API_TOKEN')
    SLACK_CHANNEL_ID = os.environ.get('SLACK_CHANNEL_ID')

    client = bigquery.Client()

    jst = pytz.timezone('Asia/Tokyo')
    today = datetime.now(jst)
    start_date = (today - timedelta(days=7)).strftime('%Y-%m-%d')
    end_date = (today - timedelta(days=1)).strftime('%Y-%m-%d')
    print(today, start_date, end_date)

    query = f"""
        SELECT 
            DATE(created_at) AS date,
            COUNT(DISTINCT session_id) AS unique_sessions
        FROM `<project>.<dataset>.summary_all`
        WHERE created_at BETWEEN '{start_date} 00:00:00' AND '{end_date} 23:59:59'
        GROUP BY date
        ORDER BY date;
    """

    query_job = client.query(query)
    results = query_job.result()

    dates = []
    session_counts = []
    for row in results:
        dates.append(row['date'].strftime('%Y-%m-%d'))
        session_counts.append(row['unique_sessions'])

    plt.figure()
    plt.plot(dates, session_counts, marker='o')
    plt.title('Unique Session Counts (Last 7 Days)')
    plt.xlabel('Date')
    plt.ylabel('Unique Sessions')
    plt.xticks(rotation=45)
    plt.tight_layout()

    image_binary = io.BytesIO()
    plt.savefig(image_binary, format='png')
    image_binary.seek(0)

    client = WebClient(token=SLACK_TOKEN)

    try:
        response = client.files_upload_v2(
            channel=SLACK_CHANNEL_ID,
            file_uploads=[{
                "file": image_binary,
                "filename": "unique_sessions.png",
                "title": "Unique Session Counts (Last 7 Days)"
            }],
            initial_comment="過去7日間の合計セッション数やで！"
        )
    except SlackApiError as e:
        return f"Error uploading file: {e.response['error']}"

    return "Success"
```

#### 必要なパッケージ
`requirements.txt`に以下を記載します。

```requirements.txt
functions-framework==3.*
google-cloud-bigquery
matplotlib
slack_sdk
pytz
```

#### 注意点
Cloud Runを利用する場合、起動元に「roles/run.invoker」ロールを割り当てる必要があります。以下のコマンドで設定します。

```bash
$ gcloud functions add-invoker-policy-binding create-weekly-total-sessions-chart \
      --region="asia-northeast1" \
      --member="MEMBER_NAME"
```

Cloud Schedulerが使用するサービスアカウントを指定する場合は`--member="serviceAccount:scheduler-account@example.iam.gserviceaccount.com"`とし、全ユーザーに公開する場合(非推奨)は`--member="allUsers"`とします。

### Cloud Scheduler
Cloud Schedulerを使用して、毎週月曜日の10:00(JST)にCloud Run関数を実行するようにスケジュールを設定します。

![スクリーンショット 2024-11-16 14.18.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/10fa8e81-e2bf-baa6-cd1e-9dc2bb65aacf.png)

### Slack API
Slack APIを開いて、新しいAppを作成します。
![スクリーンショット 2024-11-16 14.21.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/22e4182b-cb53-27b0-b0c9-97037b8bcce7.png)

OAuth & PermissionsのBot Token Scopesで、`channels:read`と`chat:write`と`files:write`のスコープを付与します。
![スクリーンショット 2024-11-16 14.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/a3ca1e41-281d-1585-dfb3-6641ac8d95a0.png)

OAuth Tokensの`Install to <workspace name>`をクリックします。
![スクリーンショット 2024-11-16 14.26.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/042f0dd7-9b4c-b638-08c4-b9e5d3ba3597.png)

Bot User OAuth Tokenをコピーします。
![スクリーンショット 2024-11-16 14.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ced1140a-7a60-65d7-69d5-fb9e6be91ff8.png)

Slack上で通知先にしたいチャンネルの設定画面を開き、インテグレーションタブの「アプリを追加する」から作成したSlack Appを追加します。
![スクリーンショット 2024-11-16 14.33.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/bc269ec5-b83a-5dca-feb1-792dc58ec467.png)

また、チャンネル情報タブからチャンネルIDをコピーします。
![スクリーンショット 2024-11-16 14.36.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/97264386-9a2f-c0dc-c060-16fd98cf3960.png)

コピーしたBot User OAuth TokenとチャンネルIDを、Cloud Run関数の`SLACK_API_TOKEN`と`SLACK_CHANNEL_ID`にそれぞれペーストして保存します。
![スクリーンショット 2024-11-16 14.38.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f7cf5a8d-dc5a-7f3a-4158-4bdc5853b291.png)

## 実行結果
毎週月曜日にSlackで以下のような通知が送信されます。過去7日間のセッション数がグラフで確認できます。
![スクリーンショット 2024-11-16 14.41.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/8754bcfb-e030-a68d-b427-1306bd220cc7.png)