Google Cloud FunctionsでGoogle Drive内の情報をSlack通知してみた②(実装編)

## 概要
Google Driveのフォルダ内にあるスプレッドシート情報をSlackに通知するCloud Functionsを試験的に作成してみたのでご紹介します。本記事は実装編です。
セットアップ編は[こちら](https://qiita.com/nagi3sho/items/c871f4d269fd8c796843)

### 構成
- Google Cloud Functions
- Google Cloud Run
- Google Drive API
- Google Sheets API
- Slack Webhook


### 全体の流れ
以下の流れで記事を進めています。本記事は3,4の紹介です。
1. Google Cloudの設定:
    - サービスアカウントの作成と必要なロールの付与
    - Workload Identity連携の設定
2. Slack Webhookの設定
3. **Cloud Functionsの実装**
    - Google Drive APIとGoogle Sheets APIを使用して、フォルダ内のファイルリストをファイル最終更新者にメンションするFunctionを作成
4. **Cloud Runでの実行と結果**

## 検証用Driveの作成
Cloud Functions実装の前に、Driveへ検証用フォルダを作成し[セットアップ編](https://qiita.com/nagi3sho/items/c871f4d269fd8c796843)で作成したサービスアカウントに共有します。
フォルダIDは実装で使用します。
```
https://drive.google.com/drive/folders/ここからフォルダID
```
![スクリーンショット 2024-12-06 23.51.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588676/73533329-ff66-74df-5b75-723b012f1c1f.png)

## Cloud Functionsの作成
「関数を作成」から設定をしてFunctionを作成していきます。
1. 関数名と認証設定
    - 「認証が必要」にしました
2. ランタイムとランタイムサービスアカウントに以下を設定：
    - ランタイム：180
    - ランタイムサービスアカウント：セットアップ編で作成したサービスアカウント
3. Build サービス アカウント
    - 「カスタム サービス アカウント」を選択しセットアップ編で作成したサービスアカウント設定
4. 接続：デフォルト設定
5. セキュリティとイメージのリポジトリ：デフォルト設定


## Cloud Functionsの実装内容
今回は検証的に以下の流れで実装しています
- Google Driveフォルダ内のファイルリストを取得(今回はスプレッドシートのみ)
- スプレッドシート最終更新者のメールアドレスを取得
- Slackでスプレッドシート最終更新者にメンションしフォルダ内のファイルリストを通知


```python
from google.auth import default
from googleapiclient.discovery import build
import requests
import json

# 認証情報を取得
def get_credentials():
    credentials, project = default()
    return credentials

# Google Drive API設定
def create_drive_service():
    credentials = get_credentials()
    return build('drive', 'v3', credentials=credentials)

# Google Sheets API設定
def create_sheets_service():
    credentials = get_credentials()
    return build('sheets', 'v4', credentials=credentials)

# 指定したフォルダ内のスプレッドシートIDを取得
def get_spreadsheet_id(folder_id):
    drive_service = create_drive_service()
    query = f"'{folder_id}' in parents and mimeType='application/vnd.google-apps.spreadsheet'"
    results = drive_service.files().list(q=query, pageSize=1).execute()
    items = results.get('files', [])

    if not items:
        raise ValueError('No spreadsheet found in the specified folder.')
    else:
        return items[0]['id']

# スプレッドシートの最終更新者のメールアドレスを取得
def get_last_updated_user_email(spreadsheet_id):
    drive_service = create_drive_service()
    revisions = drive_service.revisions().list(fileId=spreadsheet_id, pageSize=1, fields="revisions(lastModifyingUser)").execute()
    last_revision = revisions.get('revisions', [])[0]
    last_updated_user = last_revision['lastModifyingUser']
    return last_updated_user['emailAddress']

# 指定したフォルダ内のファイルリストを取得
def list_files():
    drive_service = create_drive_service()
    folder_id = 'フォルダIDを指定'
    query = f"'{folder_id}' in parents"
    results = drive_service.files().list(q=query, pageSize=10).execute()
    items = results.get('files', [])

    if not items:
        return 'No files found.'
    else:
        files_list = 'Files:\n'
        for item in items:
            files_list += f"{item['name']} ({item['id']})\n"
        return files_list

# Slackに通知を送信する関数
def send_slack_notification(message, email):
    webhook_url = 'Webhook URLを指定'
    slack_data = {
        'text': message,
        'blocks': [
            {
                'type': 'section',
                'text': {
                    'type': 'mrkdwn',
                    'text': f'<@{email}>: {message}'  # メンション形式
                }
            }
        ]
    }
    response = requests.post(
        webhook_url, data=json.dumps(slack_data),
        headers={'Content-Type': 'application/json'}
    )
    if response.status_code != 200:
        raise ValueError(f'Request to Slack returned an error {response.status_code}, the response is:\n{response.text}')

# Cloud Functionのエントリーポイントとなる関数
def cloud_function(request):
    response = list_files()
    print(response)
    # フォルダIDを指定してスプレッドシートIDを取得
    folder_id = 'フォルダIDを指定'
    spreadsheet_id = get_spreadsheet_id(folder_id)
    # スプレッドシートIDを指定して最終更新者のメールアドレスを取得
    email = get_last_updated_user_email(spreadsheet_id)
    send_slack_notification(response, email)
    return response
```

## Cloud Functionsをデプロイ後、Cloud Runで動作確認
作成したFunctionをコンソールでデプロイ後、CloudShellからcurlでFunctionを実行。
想定通りドライブ内のスプレッドシート名とスプレッドシートIDがSlack通知されました。

![スクリーンショット 2024-12-10 21.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588676/382f3e95-a1ae-7d6a-715b-d1fa5cf3d381.png)


以上です。

---
今回使用したAPIのドキュメントは以下です。どなたかのお役に立てれば幸いです。

Google Drive API

https://developers.google.com/drive/api/reference/rest/v3?hl=ja

Google Sheets API

https://developers.google.com/sheets/api/reference/rest?hl=ja