任意のSlackのチャンネルにいるメンバー全員を含んだ会議をGoogle App Scriptでさくっと作ってGoogleカレンダーにぶち込む

メーリングリストがあればいいけど、ない場合、一人ずつGoogleカレンダーに入れるのは効率が悪いしとても面倒なので、Google App Scriptでワンクリックで予定を作ってしまえば簡単だ。

## 前提

* .gsファイルを用意してあること
* SlackAppのLibrary Keyを.gsファイルのリソースに登録していること
* Slack API Access Tokenを取得し.gsファイルのリソースに登録していること

上記の３つがわからない場合は、 [初心者がGASでSlack Botをつくってみた
](https://tech.camph.net/slack-bot-with-gas/) の _2.1 Google Apps Scriptプロジェクトの準備_ / _2.2 GASにSlackAppライブラリを導入する_ / _2.3 Slack API Tokenの取得_ が参考になる。

## チャンネルメンバーのメールアドレス一覧を取得

まずは対象のSlackチャンネルの情報を取得する。Slackのよくわからない仕様で、公開チャンネルとプライベートチャンネルではGETに使うエンドポイントが異なるので注意。

```javascript:公開チャンネルの場合
/**
 * @param channelId: string // SlackチャンネルのURLに含まれる'Cxxxxxxxx'形式のID
 * return channel object // https://api.slack.com/methods/channels.info
 **/
function getChannelInfo(channelId) {
  var accessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
  var url = 'https://slack.com/api/channels.info?token=' + accessToken + '&channel=' + channelId;
  var data = JSON.parse(UrlFetchApp.fetch(url));

  return data;
}
```

```javascript:プライベートチャンネルの場合
/**
 * @param groupId: string // SlackチャンネルのURLに含まれる'Gxxxxxxxx'形式のID
 * return group object // https://api.slack.com/methods/groups.info
 **/
function getGroupInfo(groupId) {
  var accessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
  var url = 'https://slack.com/api/groups.info?token=' + accessToken + '&channel=' + groupId;
  var data = JSON.parse(UrlFetchApp.fetch(url));

  return data;
}
```

https://api.slack.com/ のドキュメントに詳細が記載されているが、いずれの場合もmembersキーにユーザIDがArrayで格納されているので、そのIDのユーザたちのメールアドレスを取得するためにユーザー一覧からメールアドレスに変換していく。

```javascript:公開チャンネルのメソッドを使った場合の例
var channelMembers = getChannelInfo('Cxxxxxxxx').channel.members;

var accessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');

var url = 'https://slack.com/api/users.list?token=' + accessToken;
var response = JSON.parse(UrlFetchApp.fetch(url));

var emails = [];
for (var i = 0; i < channelMembers.length; i++) {
  // GASはネイティブでES2015が使えないので、find() ではなくfor文でなんとかする
  for (var j = 0; j < response.members.length; j++) {
    if (response.members[j].id === channelMembers[i]) {
      emails.push(response.members[j].profile.email);
    }
  }
}
```

これでメールアドレスの一覧ができた。

## GoogleカレンダーにPOST

.gsにデフォルトで用意されている `CalendarApp` インスタンスを使って、Googleカレンダーに予定を作る。メールアドレスの一覧は、上記の `emails` をそのまま使う想定。

```javascript
var subject = 'なんとか会議'; // カレンダーのタイトル
var startTime = new Date(2018, 0, 1, 13, 0, 0); // 開始日時
var endTime = new Date(2018, 0, 1, 14, 0, 0); // 終了日時
var location = '会議室AAA'; // 会議室名など。ここもCalendarAppから取得できるが割愛
var description = '○○についての会議です\n参加してね'; // カレンダーの詳細テキスト
var sendInvites = true; // メールで通知するかどうか。デフォルトはfalseなので、trueにしないとサイレントにカレンダーだけ入れる感じになる

CalendarApp.getDefaultCalendar().createEvent(
  subject,
  startTime,
  endTime,
  {
    location: location,
    description: description,
    guests: emails.join(','),
    sendInvites: sendInvites
  }
);
```

これでOK。 `guests` のところはstringでメールアドレスをカンマ区切りの文字列にするだけなのでjoinしている。

ほかにも、終日の予定は `createEvent()` ではなく `createAllDayEvent()` だったり、繰り返しの予定は `newRecurrence().addDailyRule()` のように独自のメソッドを持っていたりするので、仕様書 https://developers.google.com/apps-script/reference/calendar を読めば応用はできる。

.gsファイルの実行者がカレンダーのオーガナイザーとなるので、別の人のかわりに実行してあげる場合はカレンダーが生成されたあとでchange ownershipしないといけないので注意。