Slack + GASで、サクッとMeet会議リンクを生成しよう

# 😇 Meetのリンクをサクッと作りたい

新型コロナの影響でリモート業務が増えているであろうtec界隈。
リモート会議用のツールは、ZoomやTeamsなどいろいろあると思いますが、今回はMeetのお話です。

SlackからMeetのリンクをサクッと作りたいって思ったことないですか？
Meetのページ開いて、会議を開始して、URLを参加者に配って、、って結構かったるい。だからと言って、いちいち文章に落とし込んで送るのもめんどくさい。
口頭で2,3分で終わるような話だって、気軽にサクッと通話して解決できた方がいいですよね。

というわけで、 Slackとgasを使って、**Meetの会議用リンクを生成してくれるボット**を作っていきます！

## ✨ 完成イメージ
「Meetガチャ」っていうと、Meetの部屋のリンクが送られてきます！🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/714d12e8-d961-5c97-12bd-fc4ff9c2c2da.png)
後半では`/meet`で呼び出せるコマンドも作りますよ！🔥

## 📚 必要なもの(?)
* GoogleAppsScript
* SlackApp （https://api.slack.com/apps）
* OutgoingWebhook

全部ブラウザ経由でできちゃいます。

## 💪 GASでMeet会議リンクを生成する
いきなりメイン部分です。gasを使って、Meetの会議リンクを取得します。

### 1. GoogleAppsScriptを開く
https://script.google.com/create にアクセスして、新しいscriptを作ります。

### 2. 以下のコードを貼り付け
スニペットにそのまま貼り付けちゃってください（ちなみにこの時点では未完成です）。

```javascript:getMeetUrl.gs
function getMeetUrl() {
  const calendarId = 'primary'; // 一時的にイベントを作成するカレンダーのID。わからない人はこのままでOK
  const dt = new Date(); // 日付は適当に設定
  const date = dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate();
  const requestId = Math.random().toString(32).substring(2); // 適当な文字列を作る
  const response = Calendar.Events.insert({
    summary: "temp_event",
    singleEvents: true,
    allDayEvent: true,
    start: {
      date,
    },
    end: {
      date,
    },
    conferenceData: {
      createRequest: {
        requestId,
        conferenceSolutionKey: {
          type: "hangoutsMeet"
        },
      }
    }
  },calendarId, { conferenceDataVersion: 1 })

  Calendar.Events.remove(calendarId, response.id)
  
  if (response.conferenceData.createRequest.status.statusCode === "success") {
    const meetUrl = response.conferenceData.entryPoints[0].uri
    Logger.log(meetUrl)
  }
}
```

### 3. Google Calender APIと接続する
`Resources > Advanced Google services....`から、`Calendar API`を**ON**にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/361ab281-9fc3-fe72-2034-14fc525dbf58.png)

### 4. テストしてみる
ここで一回動かしてみましょう。
実行関数が`getMeetUrl`になっていることを確認して、Playボタンを押します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/fb5e259b-4750-05c1-00da-207f0b027d7f.png" width="600px" />

初回実行時は権限に関するダイアログが出てくると思いますが、`Continue`→`Accept`を押して承認します。
エラーが起きない場合何も出てこないと思いますが、 `View > Logs`を押すと、下のような画面が出てきて、meetのリンクが取得できていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/521e0c34-1523-a561-26c1-3df8eacc3cac.png)

gasでやってること自体は単純で、
1. Google Calendar APIで、会議付きの予定を作る
2. 会議リンクを取得する
3. 予定を削除する
の3つだけです。

Meetの会議リンクは、一度生成すると予定を削除しても使えてしまうので、空のカレンダーを作成してリンクだけ取得します。

## 🤝 Slackと連携する

### 1. Outgoing WebHooksの準備
Slackのサイドバーにある`App`から、`Outgoing WebHooks`を探して追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/8a62c769-3a46-ef4e-9276-1415ffe2e867.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/7e7c5dc6-9b9a-cf1c-e51c-f486156e7e80.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/900736fb-fe42-21a0-9b49-f6573af95dcf.png)

この時点で、トリガーワードを設定しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/f322d621-b3ef-be2d-7cdd-9c631637a899.png)
**URL**は一旦空のままで大丈夫です。
**トークン**は後で使うので、コピーしておきましょう。

### 2. SlackAppを作る

`Create an App`を押して、アプリを作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/0b12ce21-439e-ab85-21f5-27add8cf54b8.png)

アプリ名を適当につけて、Workspaceを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/fa06551c-a860-c2aa-917f-9ba594c1c860.png)

**Bots**から、Botユーザを追加します。`Add Display Name`から表示名などを追加するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/c1189923-1624-a40e-cb01-fc4e6a9a54f7.png)

次に、Botに渡す権限を指定します。
メッセージの書き込みをするので、`chat:write`と`chat:write.public`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/22e69580-a85d-d819-c90a-cc93a00f0f5e.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/99f12d1b-244b-62a6-b9a0-b14a4c23427c.png)

権限の設定が終わると`Install App to Workspace`が押せるようになっているので、クリックしてアプリの連携を承認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/fb3a9496-1bfa-250f-0091-31719c67714e.png)

ようやくトークンが発行されました。 この**Bot User OAuth Access Token**はコピーしておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/f0531135-ef32-f481-0c05-1da057b8faab.png)


### 3. GASにライブラリを追加する
とても便利そうなライブラリを見つけたので、ありがたく使わせていただきます🙇🏻‍♂️（https://qiita.com/soundTricker/items/43267609a870fc9c7453）。

`Resources > Libraries...`を開いて、次の値で検索して追加をします。
`M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/d7a555d5-491c-38e5-6a8f-16acdd434237.png)
2020/08時点では最新バージョンは`22`でした。

次に、 `File > Project properties`の`Script properties`を開いて、トークン情報を記入していきます。

| Property name | Token |
|---|---|
| `SLACK_ACCESS_TOKEN` | Appを作った時にコピーした**Bot User OAuth Access Token** |
| `OUTGOING_WEBHOOK_TOKEN` | Outgoing Webhook連携時にコピーした**トークン** |
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/6677daee-50c1-ab90-499d-656c50b5bc62.png)

### 4. GASのコードを追加する
Slackから呼び出すためのdoPost関数を作ります。
先ほど書いたコードは消して、以下のコードに上書きしてしまってください。

```javascript:meet_gacha.gs
function getMeetUrl() {
  const calendarId = 'primary';
  const dt = new Date();
  const date = dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate();
  const requestId = Math.random().toString(32).substring(2);
  const response = Calendar.Events.insert({
    summary: "temp_event",
    singleEvents: true,
    allDayEvent: true,
    start: {
      date,
    },
    end: {
      date,
    },
    conferenceData: {
      createRequest: {
        requestId,
        conferenceSolutionKey: {
          type: "hangoutsMeet"
        },
      }
    }
  },calendarId, { conferenceDataVersion: 1 })
  
  Calendar.Events.remove(calendarId, response.id)
  
  if (response.conferenceData.createRequest.status.statusCode === "success") {
    return response.conferenceData.entryPoints[0].uri
  }
}

function doPost(e) {
  const token = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN')
  const verifyToken = PropertiesService.getScriptProperties().getProperty('OUTGOING_WEBHOOK_TOKEN')

  if (verifyToken != e.parameter.token) {
    throw new Error("invalid token.")
  }

  const meetUrl = getMeetUrl()
  const message = meetUrl !== undefined ? `Meetの部屋を作ったよ\n${meetUrl}` : "URL生成できなかった。ごめんね"
  
  const slackApp = SlackApp.create(token)
  slackApp.chatPostMessage(e.parameter.channel_id, message)
}
```

最後に、公開作業です。
`Publish > Deploy as web App`を開いて、アクセス可能ユーザーを匿名も可にしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/b403491c-920b-24b1-5bde-c63255213d71.png)

URLが生成されるので、コピーしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/07343de7-348c-2b06-5f08-0585a75b9c6d.png)

Outgoing Webhookのページに戻り、コピーしたURLを貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/4987f496-1508-55f2-e512-71eab9c1cbb4.png)

これで完成！🎉
Slackでトリガーワードを呟いてみましょう。

## 💥 プライベートチャンネルやDMで使えない
このままでも十分なんですが、Outgoing WebhookはプライベートチャンネルやDMでは動いてくれません。
応用編として、コマンドを作ってみましょう。

### 1. Slash Commandを登録する
Appを作った時のページを開いて、`Slash Command`を登録します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/d8f04d67-1ab9-3c86-1f6e-3dc16fbba9e3.png)

好きなコマンドを入れましょう。
**Request URL**は、先ほど公開したGASのリンクを貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/60ce677a-61a7-5bee-9ec5-fcc7e19b490e.png)


SlackBotが更新されたから再インストールしてって言われるので、再インストールしてあげましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/0c4d5113-f764-9964-4654-ef40057e7532.png)

ついでに、トークンをコピーしておきましょう。
`Basic Information`のページから**Verification Token**の箇所にあるトークンです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/6d0aa990-e48d-aee0-002b-95fec44c46d6.png)

### 2. GASのスクリプトを修正
次に、GASを`Slash Command`に対応させましょう

```javascript:meet_gacha.gs
function getMeetUrl() {
  const calendarId = 'primary'; // 一時的にイベントを作成するカレンダーのID
  const dt = new Date(); // 日付は適当に設定
  const date = dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate();
  const requestId = Math.random().toString(32).substring(2); // 適当な文字列を作る
  const response = Calendar.Events.insert({
    summary: "temp_event",
    singleEvents: true,
    allDayEvent: true,
    start: {
      date,
    },
    end: {
      date,
    },
    conferenceData: {
      createRequest: {
        requestId,
        conferenceSolutionKey: {
          type: "hangoutsMeet"
        },
      }
    }
  },calendarId, { conferenceDataVersion: 1 })
  
  Calendar.Events.remove(calendarId, response.id)
  
  if (response.conferenceData.createRequest.status.statusCode === "success") {
    return response.conferenceData.entryPoints[0].uri
  }
}

function doPost(e) {
  const slackAccessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN')
  const outgoingWebhookToken = PropertiesService.getScriptProperties().getProperty('OUTGOING_WEBHOOK_TOKEN')
  const slackAppToken = PropertiesService.getScriptProperties().getProperty('SLACK_APP_TOKEN')
  const verifyTokens = [outgoingWebhookToken, slackAppToken]
  
  const requestToken = e.parameter.token
  if (!verifyTokens.includes(requestToken)) {
    throw new Error("invalid token.")
  }
  
  const meetUrl = getMeetUrl()  
  const message = meetUrl !== undefined ? `Meetの部屋を作ったよ\n${meetUrl}` : "URL生成できなかった。ごめんね"

  if (requestToken === outgoingWebhookToken) {
    const slackApp = SlackApp.create(slackAccessToken)
    slackApp.chatPostMessage(e.parameter.channel_id, message)
  } else if (requestToken === slackAppToken) {
    return ContentService.createTextOutput(JSON.stringify({text: message, response_type: 'in_channel'})).setMimeType(ContentService.MimeType.JSON);
  }
}
```

また、先ほどコピーしたトークンをプロパティに追加します。
キーは `SLACK_APP_TOKEN`としてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/36c8a06b-4569-716d-d697-0a1f0f3cd0b1.png)

### 3. 公開版を更新して完成
**Project version**は、必ず`New`にしてください（じゃないと更新されない）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/1077102d-0a1c-4ea3-0d6d-2503d8dee484.png)

これで`Slash Command`が使えるようになりました！🎉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265811/13b45783-c1bf-63ed-26e4-3210e2cea12b.png)

# 🌟 まとめ
SlackからサクッとMeetのリンクを発行することができるようになりました！🎉🎉🎉
参加者に共有するためにURLをコピペする必要がないってのが、個人的にとても嬉しい。

……嬉しいんですが、僕が今働いている会社はTeamsが主流なんですよね🥺（悲しい）

GSuiteをお使いの方は、ぜひ使ってみてください！！

## 参考
実はGASをまともに触るの初めてで、いろんな方の記事を参考にさせていただきました🙇🏻‍♂️
https://qiita.com/aromanokarisu/items/d7fd00d467812f20e25f
https://qiita.com/soundTricker/items/43267609a870fc9c7453
https://qiita.com/pistaman/items/a542119ea28871960477
https://qiita.com/chikuwa111/items/7a1a349b82318a5861cc