Slackの特定チャンネルで一定期間を過ぎた昔の投稿を定期的に削除する

# はじめに

勤務する会社ではSlackを利用しています。話題の[分報](http://c16e.com/1511101558/)を導入したところ、投稿されるメッセージ数が多くなり、フリープランの「10,000件制限」で過去の投稿が表示できなくなることが多くなりました。

「有効な投稿が流れちゃうので...、もう少し長期間表示されるようにできませんか？」

通知目的で利用しているチャンネル[^1]には、無駄なメッセージが残っているね...。ということで、特定チャンネルに対し、ある程度昔の投稿を、自動的に削除するツールを作ってみることにしました。

[^1]: 各プロジェクトごとのチャンネルを用意し、Jenkinsのビルド結果、Redmineのチケット更新、ソース管理へのコミットメッセージなど、業務中に把握しておきたい情報が通知されるようにしています

# 方針

やりたいことは、次の通り。

- 一定期間（とりあえず1週間）を過ぎた過去の投稿を定期的に削除する
- 対象のチャンネルは指定したい（複数）

Excelライクでメンテナンスが容易、さらに定期実行するスクリプトも書けるので、Googleスプレッドシートを利用することにしました。

# Googleスプレッドシート

まず、対象チャンネルですが、以下のフォーマットで指定することにしました（チャンネル名の頭の#は無しで）。

![Channel.png](https://qiita-image-store.s3.amazonaws.com/0/60193/d00647c4-65f8-10c0-6a50-9b07e4d8dd7e.png)

# Google Apps Script

## [SlackApp](https://qiita.com/soundTricker/items/43267609a870fc9c7453)をライブラリへ追加する

Slackを外部から制御するので、[Slack API](https://api.slack.com/methods)を利用します。このSlack APIをGoogle Apps Scriptから簡単に呼び出すため、SlackAppライブラリ[^2]を利用させていただきました。

[^2]: Slack APIのGoogle Apps Scriptラッパ

[基本情報](https://qiita.com/soundTricker/items/43267609a870fc9c7453#%E5%9F%BA%E6%9C%AC%E6%83%85%E5%A0%B1)にあるLibrary Keyを、[リソース]-[ライブラリ]内の[ライブラリを追加]へ入力して[追加]することで利用できるようになります。バージョンは22を使用しました。

![Library.png](https://qiita-image-store.s3.amazonaws.com/0/60193/644e39b2-00db-3ffe-da4c-e53861334c91.png)

## SlackのLegacy tokenを発行する

SlackAppを利用するのに必要なので、Slackの[Legacy tokens](https://api.slack.com/custom-integrations/legacy-tokens)のページでトークン（xoxp-xxxxxxxxxx-xxxxx...といった感じのやつ）を発行しておきます。

また、このトークンはスクリプト内で利用するので、[ファイル]-[プロジェクトのプロパティ]内の[スクリプトのプロパティ]へ、SLACK_API_TOKENとして追加しておきました。

![Property.png](https://qiita-image-store.s3.amazonaws.com/0/60193/8403dfe8-6444-8190-bad2-e7e83aa739b6.png)

## スクリプトを実装する

続いてスクリプトです。まず、スプレッドシートに記載されているチャンネル名を取得します。そして、トークンを渡して作成したSlackAppとともに、下位関数を呼び出します。

```js:cleaner.gs
function cleanChannels()
{
  var sheet = SpreadsheetApp.getActiveSheet();
  var values = sheet.getDataRange().getValues();

  var channelNames = [];
  for (var i = 1; i < values.length; ++i) {
    channelNames.push(values[i][0]);
  }

  var token = PropertiesService.getScriptProperties().getProperty('SLACK_API_TOKEN');
  var slackApp = SlackApp.create(token);

  for each(var channelName in channelNames) {
    cleanChannel(slackApp, channelName);
  }
}
```

呼び出された下位関数では、「1週間前の投稿を取得（channelsHistory()）→ 削除（chatDelete()）」を、投稿が取得されなくなるまで繰り返します。

```js:cleaner.gs
function cleanChannel(slackApp, channelName)
{
  var channelId = getChannelId(slackApp, channelName);
  if (channelId.length == 0) {
    return;
  }

  var date = new Date();
  date.setDate(date.getDate() - 7); // 1週間前
  var timestamp = Math.round(date.getTime() / 1000) + '.000000';

  do {
    var optParams = {
      latest: timestamp,
      count: 1
    };
    var result = slackApp.channelsHistory(channelId, optParams);
    if (result.ok) {
      for each(var message in result.messages) {
        slackApp.chatDelete(channelId, message.ts);
      }
    }
  } while (result.ok && result.has_more)
}
```

途中、チャンネル名からチャンネルIDを取得していますが、次のように実装しました。

```js:cleaner.gs
function getChannelId(slackApp, channelName)
{
  var channelId = '';
  var result = slackApp.channelsList();
  if (result.ok) {
    for each(var channel in result.channels) {
      if (channel.name == channelName) {
        channelId = channel.id;
        break;
      }
    }
  }
  return channelId;
}
```

## プロジェクトのトリガーを設定する

最後に、[編集]-[現在のプロジェクトのトリガー]から、上記のcleanChannels()を適当なタイミングで指定すれば終了です。とりあえず、1日4回実行するように設定しました。

![Trigger.png](https://qiita-image-store.s3.amazonaws.com/0/60193/e5e688d2-36cc-d34a-8bdf-e4bff5226043.png)

# おわりに

なんとなくですが、以前と比べると、過去の投稿に対して「あ、まだ参照できた」と思うことが多くなったように思います。スタンダードプランへ移行するまでの中継ぎとして、多少は活用できそうだな、と思っています。