SANANEBLOG
GAS PR

Slackに投稿された添付ファイルを自動でGoogleドライブに保存し記録する【GAS】

記事内に商品プロモーションを含む場合があります

はじめに

今回の記事では、Slackのメッセージに添付されたファイルを自動的にGoogleドライブに保存し、ログをGoogleスプレッドシートに記録する方法を紹介します。

Slackは、無料プランでは90日しかメッセージやファイルを保存できないことや、重要なファイルや情報を検索したり手動で保存するのは時間がかかり、効率的ではありません。

そこで今回は、Google Apps Script(GAS)を使って、この機能を実装します。

メリット
  1. 時短:手動でのファイルのダウンロードやアップロードの手間を省くことができます。
  2. 整理と管理:すべてのファイルがGoogleドライブに保存され、その詳細がスプレッドシートに自動的に記録されるため、情報の追跡や管理が容易になります。
  3. カスタマイズ可能:特定のファイルタイプだけを対象にする、またはすべてのファイルを対象にするなど、ニーズに合わせてスクリプトをカスタマイズすることができます。

動作イメージ

SANANE

まずは簡単に本ツールの動作について説明します。

PDFを例に取っていますが、ファイル全般の保存が可能です。

保存用Googleドライブ

まず、Googleドライブ上にファイルを保存したいフォルダを用意します。

記録用スプレッドシート

ファイルを保存したことを記録するスプレッドシートは以下のようになっています。

記録できる項目は以下となります。

  • 投稿時刻
  • 投稿者
  • ファイルと一緒に投稿されたメッセージ
  • ファイル名
  • ファイルURL(Googleドライブ上のファイルURL)

Slack

ファイルを投稿したいチャンネルにslackのアプリを追加しておき、テスト投稿としてメッセージにファイルを添付して投稿します。

複数添付ファイルや、メッセージの返信に添付されたファイルにも保存できます。

GAS上で指定した時間ごとにプログラムが実行されます。

このとき、Slack上に新しいファイルが投稿されていると、自動でGoogleドライブにファイルを保存します。

同時にスプレッドシート上で保存したファイルの情報が記録されます。

作成方法

本自動化処理は以下の流れで作成します。

  1. Slack APIの作成
  2. Slack Appをチャンネルに追加する
  3. Googleドライブ上にフォルダ作成、 スプレッドシートの作成
  4. GASの作成、定期実行トリガーの作成

Slack API作成

まず、SlackのAPIにアクセスします。このとき、自動化したいチャンネルがあるワークスペースでログインします。

[Create New App] をクリックし、[From scratch]をクリックします。

任意のアプリ名を入力、ワークスペースを選択し、 [Create App]をクリックします。

スコープの設定

左側のメニューより、[OAuth & Permissions]をクリックします。

[Scopes]セクションにて、下記画像のようにBot Token Scopesに以下4つをスコープとして[Add an OAuth Scope]をクリックして登録してください。

  • channels:history
  • channels:read
  • files:read
  • users:read

アプリをワークスペースにインストールする

スコープを設定した後、[OAuth & Permissions]の一番上のページにある[Install to Workspace]をクリックします。

権限をリクエストされるため、[許可する]をクリックします。

インストール完了後は、Bot User OAuth Token が生成されますのでコチラをコピーして控えておいてください。

Slack Appをチャンネルに追加する

Slackに移動し、今回の自動化処理を適用したいチャンネルに移動します。

メッセージ入力欄にて「/」と入力することでショートカットが開きます。

左メニューに新しくインストールしたAppがあることを確認し、[このチャンネルにアプリを追加する]をクリックします。

今回作成したアプリを選択して、[追加]をクリックします。

Googleドライブ フォルダの作成

Googleドライブを開き、PDFを保存したいフォルダを作成します。

作成したら、GoogleドライブのフォルダIDをコピーして控えておきます。

フォルダIDは対象のフォルダのアドレスバーから、以下「ここをコピーして控えておく」の部分になります。

https://drive.google.com/drive/folders/ここをコピーして控えておく

Googleスプレッドシートの作成

Googleスプレッドシートにアクセスします。

作成したスプレッドシートの拡張機能からApps Script をクリックします。

GASの作成

GASのスクリプトエディタが別タブで開きます。

Google Drive サービスの追加

左メニューの[サービス] > [Drive API]をクリック、[追加]をクリックします。

スクリプトのペースト

もともと記載してある以下コードは削除します。

function myFunction() {

}

代わりに、以下のコードをコピーしてペーストします。

このとき、2〜4行目の各” ”で囲まれたところに控えておいたトークンやIDに書き換えます。

YOUR_SLACK_BOT_TOKEN:Bot User OAuth Token

YOUR_CHANNEL_ID:記録を行いたいSlackのチャンネルのID

YOUR_GOOGLE_DRIVE_FOLDER_ID: GoogleドライブのフォルダID

Slack チャンネルIDの探し方 はじめに Slackを使用している方々、特にSlack APIを利用してBotなどを開発している方にとって、チャンネルIDはとき...
function savePdfToDrive() {
    var token = "YOUR_SLACK_BOT_TOKEN"; 
    var channel = "YOUR_CHANNEL_ID"; 
    var driveFolderId = "YOUR_GOOGLE_DRIVE_FOLDER_ID";
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  
    // ヘッダーの設定
    if (sheet.getRange("A3").getValue() === "") {
      sheet.getRange("A3").setValue("投稿時刻");
      sheet.getRange("B3").setValue("投稿者");
      sheet.getRange("C3").setValue("投稿内容");
      sheet.getRange("D3").setValue("ファイル名");
      sheet.getRange("E3").setValue("ファイルURL");
    }
    
    var lastExecutionTime = sheet.getRange("A1").getValue();
    var apiUrl = "https://slack.com/api/conversations.history?channel=" + channel + "&limit=100";
  
    var options = {
      "method": "get",
      "headers": {
        "Authorization": "Bearer " + token
      }
    };
  
    var response = UrlFetchApp.fetch(apiUrl, options);
    var data = JSON.parse(response.getContentText());
  
    for (var i = 0; i < data.messages.length; i++) {
        var message = data.messages[i];
  
        // メインメッセージの処理
        processMessage(message, token, driveFolderId, sheet, lastExecutionTime);
        
        // 返信がある場合、それらの返信を処理
        if (message.reply_count > 0) {
            var repliesApiUrl = "https://slack.com/api/conversations.replies?channel=" + channel + "&ts=" + message.ts;
            var repliesResponse = UrlFetchApp.fetch(repliesApiUrl, options);
            var repliesData = JSON.parse(repliesResponse.getContentText());
            for (var j = 0; j < repliesData.messages.length; j++) {
                processMessage(repliesData.messages[j], token, driveFolderId, sheet, lastExecutionTime);
            }
        }
    }
  
    sheet.getRange("A1").setValue(new Date());
}

function processMessage(message, token, driveFolderId, sheet, lastExecutionTime) {
    var messageTimestamp = new Date(message.ts * 1000);
  
    if (message.files && messageTimestamp > lastExecutionTime) {
        for (var j = 0; j < message.files.length; j++) {
            var file = message.files[j];
            
            var fileUrl = file.url_private_download;

            // ユーザ情報の取得
            var userApiUrl = "https://slack.com/api/users.info?user=" + message.user;
            var userResponse = UrlFetchApp.fetch(userApiUrl, {
                "method": "get",
                "headers": {
                    "Authorization": "Bearer " + token
                }
            });
            var userData = JSON.parse(userResponse.getContentText());
            var userName = userData.user ? userData.user.real_name : "Unknown User";
          
            Logger.log("File URL: " + fileUrl);

            if (!fileUrl) {
                Logger.log("URL not found for file: " + file.name);
                continue; 
            }

            var fileOptions = {
                "method": "get",
                "headers": {
                    "Authorization": "Bearer " + token
                }
            };

            try {
                var fileResponse = UrlFetchApp.fetch(fileUrl, fileOptions);
                var blob = fileResponse.getBlob().setName(file.name);
                var savedFile = DriveApp.getFolderById(driveFolderId).createFile(blob);
                var driveUrl = "https://drive.google.com/file/d/" + savedFile.getId() + "/view";

                // 指定した情報をスプレッドシートに追加
                var nextRow = sheet.getLastRow() + 1;
                if (nextRow < 4) nextRow = 4;
                sheet.getRange(nextRow, 1).setValue(messageTimestamp);
                sheet.getRange(nextRow, 2).setValue(userName);
                sheet.getRange(nextRow, 3).setValue(message.text);
                sheet.getRange(nextRow, 4).setValue(file.name);
                sheet.getRange(nextRow, 5).setValue(driveUrl);
            } catch (error) {
                Logger.log("Error downloading or saving the file: " + file.name);
                Logger.log(error.toString());
            }
        }
    }
}

上部メニューの[プロジェクトを保存]ボタンをクリックして保存します。

トリガーの作成

SANANE

最後に定期的に実行するためのトリガーを作成します。

GASスクリプトエディタの左部のメニューから「トリガー」(時計のアイコン)をクリックし、画面下部の「+ トリガーを追加」をクリックします。

実行する関数を[savePdfToDrive]に設定し、イベントソースを[時間主導型]に、時間ベースのタイマーのトリガーのタイプを選択では[時間ベースのタイマー]に設定します。

時間の間隔を選択では[1時間おき]等実行したい間隔を選択して保存をクリックします。

【補足】トリガー保存時にエラーが発生した場合は、スクリプトがうまく保存されていない場合がありますので、再度コードを書く画面に戻ってブラウザの更新をしてください。その後再度スクリプトをコピー&修正してください。

最初の関数の実行には権限が必要となるため、[承認が必要です]というモーダルが表示されたら、

[権限を確認]>[表示されているGoogleアカウント(Choose an account)]>[詳細(Advanced)]>[Go to 無題のプロジェクト(unsafe)]>[許可(Allow)]まで移動します。

クリック後トリガーが作成されます。

動作テスト

GASの左部のメニューから「エディタ」をクリックします。

[▶実行]をクリックします。

問題なく実行が完了すると、作成したスプレッドシートに最終実行時間と記録のヘッダーが追加され、ファイルがGoogleドライブに保存されます。

A1セルに時刻が入力されていない状態で実行すると、指定したチャンネルの直近100件のメッセージを検索し、そのメッセージに添付されているファイルを保存します。

その後、A1セルに実行した時刻が記入されます。

GASおすすめ本

GASをもっと勉強したい!ほかにも自分で何か作成したい!という方には以下がおすすめです。仕事で使えるアイデアなども得ることができます。