Site cover image

Site icon image blog by izumiz

blog by izumiz

Node.jsでSlackチャンネルのファイルを一括ダウンロードする

目的

Slackのチャンネルにアップされたファイルのバックアップをとるため

事前準備

Slack アプリ設定
  1. https://api.slack.com/apps から”Create New App”
  2. AppNameとWorkspaceを指定し,“Create App”
  3. OAuth&Permissionsへ
  4. ScopesからBot Token ScopesでAdd an OAuth Scopeでfiles:readを追加
  5. Install your appからWorkspaceにアプリをインストール
  6. ダウンロードしたいチャンネルにアプリを追加
.envファイルの設定
.env ファイルを作成
cp .env.example .env
トークンの設定

OAuth&PermissionsのBot User OAuth Tokenをコピーし,TOKEN=の後に張り付け.

チャンネルIDの設定

Slackアプリから,チャンネルのリンクをコピーしURLの末尾のIDをCHANNEL=の後に張り付け

アプリ使用

git clone git@github.com:izumiz-dev/slack-file-downloader.git

npm install で依存関係をインストール

mkdir downloads ダウンロードフォルダの作成

npm start でダウンロード開始

内容説明

  • axios
  • Slack Web Apis

を使用して作成しました.
ライブラリとして Slack Web API は 必ずしも使用する必要はありませんが,今回はなんとなく使いました.

.envからの読み込み
// OAuth Tokens from .env file
const TOKEN = process.env.SLACK_TOKEN;
const CHANNEL = process.env.CHANNEL;

dotenvというライブラリを用いて,.envファイルから環境変数を読み,nodeスクリプトを実行します.
そのためpackage.jsonscriptsは以下のようになっています.

  "scripts": {
    "start": "node -r dotenv/config index.js",
  }
チャンネルのファイル一覧の取得

指定したチャンネルのファイルのリストを取得します.countはデフォルトでは100件のため今回は1000にしました.

  const res = await web.files.list({
    channel: CHANNEL,
    count: 1000
  })
ファイルダウンロード

まず,取得したファイル一覧のダウンロードリンクと,タイトル名のオブジェクトの配列 downloadFiles を作成.

  const downloadFiles = res.files.map(file => {
    return {
      url: file.url_private_download,
      name: file.name
    }
  });

次にその配列のオブジェクトにあるダウンロードリンクに対してGETリクエストを送りファイルをダウンロードします.
非同期関数内であるため, Promise.all の中で map をし,更に setTimeout を使い1秒毎にダウンロードするようにしました.

headerに認証情報を渡してあげないとダウンロードできません.responseType'stream'に指定.

リクエスト自体はシンプルです.

  const res = await axios({
    method: 'get',
    url: file.url,
    headers: {
      Authorization: `Bearer ${TOKEN}`,
    },
    responseType: 'stream'
  });

ダウンロードしたデータを,Node.js のfs.createWriteStream()によって保存.

  const savePath = path.resolve(__dirname, 'downloads', file.name)
  console.log(`Saving: ${file.name}`)
  res.data.pipe(fs.createWriteStream(savePath))

これで,downloadsフォルダに保存されていきます.

ソースコード

const axios = require('axios');
const fs = require('fs');
const path = require('path');
const { WebClient } = require('@slack/web-api');

// OAuth Tokens from .env file
const TOKEN = process.env.SLACK_TOKEN;
const CHANNEL = process.env.CHANNEL;
const web = new WebClient(TOKEN);

(async () => {

  try {
    const res = await web.files.list({
      channel: CHANNEL,
      count: 1000
    })

    const downloadFiles = res.files.map(file => {
      return {
        url: file.url_private_download,
        name: file.name
      }
    });

    Promise.all(downloadFiles.map(async (file, index) => {
      setTimeout(async () => {
        const res = await axios({
          method: 'get',
          url: file.url,
          headers: {
            Authorization: `Bearer ${TOKEN}`,
          },
          responseType: 'stream'
        });
        const savePath = path.resolve(__dirname, 'downloads', file.name)
        console.log(`Saving: ${file.name}`)
        res.data.pipe(fs.createWriteStream(savePath))
      }, 1000 * index)
    }))
  } catch (error) {
    console.log(error);
  }
})();

学んだこと

Slack の WebApi は,以前使用したことがありました.
しかし,ファイルの保存は fs.writeFileSync() くらいしか知らなかったので勉強になりました.

最終的にはググって StackOverFlow をコピペすれば,動くものが作れるのでインターネット様様です😅

参考リンク