Google Apps Script でドキュメントリンクを Slack に通知する

# はじめに
[毎週行っている作業](https://qiita.com/h-yuki/items/4ba27e415a740d10b870)を自動化する Google Apps Script の、[基本設計](https://qiita.com/h-yuki/items/e00b8743968606582f99)に示した4つ目のステップ「雛型ファイルのリンクを [Slack](https://slack.com/) に通知する」について説明します。

HTMLの雛型ファイル生成については、[テンプレートを使って生成する方法](https://qiita.com/h-yuki/items/4b7980a273079ca7a140)を説明しました。サンプルコードの``createHtmlTemplate()``関数は [File](https://developers.google.com/apps-script/reference/drive/file) 型のオブジェクトを返しますので、この記事では File オブジェクトの URL を Slack に通知する関数を作成します。

# 関数仕様

## 関数名: slackSendTemplate
- 引数
    - file : URL を通知する対象 [File](https://developers.google.com/apps-script/reference/drive/file)
- 返り値： なし
- 振る舞い
    - file の URL を Slack に通知する

シンプルですね。でも何か足りませんね。はい、一言に Slack と言いましても、どのワークスペースのどのチャネルに投稿するのかを示すパラメータが必要です。チャネルは開発中と本番運用で分けたいので、このパラメータはデプロイ時に決まると考えられます。[こちら](https://qiita.com/h-yuki/items/a16fd13602af2039ee43#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E6%99%82%E3%81%AB%E6%B1%BA%E5%AE%9A%E3%81%99%E3%82%8B%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF)でも説明したように、このようなパラメータはプロジェクトのプロパティとして定義するのがよいです。

## Slack の Webhook
外部アプリと連携して Slack にメッセージを送信する最も簡単なインターフェイスは [Webhook](https://api.slack.com/incoming-webhooks) です。Slack が提供するエンドポイントに対して、JSON 形式のデータをポストするだけで、Slack に通知を流すことができます。

### Webhook の設定

[こちら](https://api.slack.com/incoming-webhooks)のドキュメントに簡単なチュートリアルがありますので、それを見て設定して下さい。英語がしんどいですか? では、簡単に手順を書いておきます。

- 上記リンク先の右上 "Your Apps" というリンクをクリックします
- "Create New App" をクリックします
- 表示されたダイアログにアプリ名を入力し(この記事では AirBot とします)、Slack のワークスペースを選択します(サインインしているワークスペースを選択するか、ここでワークスペースにサインインします)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/4cbcb483-2b27-4ccf-acd3-7384621c9ed7.png)

- "Create App" をクリックします
- "Incoming Webhooks" をクリックします
![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/ac40db94-9da9-47e1-afe4-c699d94c6329.png)
- Webhook を有効化します(Off → On)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/04d728d2-e58a-fff0-4170-6a6efbb6ad0f.png)
- On にすると以下が表示されますので、"Add New Webhook to Workspace" をクリックします
![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/1be95044-0775-a0b3-dfc5-e7609a56d04f.png)
- 開いた画面で投稿先のチャネルを選択して、"許可する" をクリックします
- 前の画面に戻りますが、Webhook URL の表示が増えていますので、"Copy" ボタンをクリックして URL をコピーします(必要な時にいつでもコピーできます)
- サンプルコードもこの URL が挿入されたコードに変わっていますので、"Copy" ボタンをクリックしてコピーし、[curl](https://curl.haxx.se/)がインストールされた環境のターミナルに貼り付けて実行してみて下さい(感動の瞬間です！)

以上で完了です。Slack に投稿するのに必要なのはこの Webhook URL だけです。つまり、URL を知っていれば誰でも投稿できてしまいますので、URL をソースコードに埋め込むようなことはしないで下さいね。万一漏洩してしまった場合は、一旦ゴミ箱のアイコンをクリックして削除し、作り直して下さい。

### プロパティ: SLACK_WEBHOOK

先にも書きましたように、Webhook URL はデプロイ時に決まるものと考えるとよいので、プロパティを作成します。プロパティ名は SLACK_WEBHOOK とします。

- Google Apps Script のスクリプトエディタで、メニューの「ファイル」→「プロジェクトのプロパティ」を選択します
- 開いたダイアログの「スクリプトのプロパティ」タブをクリックします
- 「+行を追加」をクリックします
- (名前)に SLACK_WEBHOOK を、(値)に Webhook URL を入力し、「保存」をクリックします
![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/d0d01b80-dd05-d7be-b91c-c61b34c327d7.png)

# テストコード
マイドライブに HTML ファイルを作成して、その URL を Slack に通知するテストです。

```javascript:test.gs
function testSlackSendTemplate() {
  const file = DriveApp.createFile('template.html', '<h1>This is a template file.</h1>', MimeType.HTML);
  slackSendHtml(file);
}
```

# 関数コード
## シンプルなメッセージ
まずは、何の飾り気もなく、ただ作成したファイルの URL を Slack に通知するコードです。Webhook URL に JSON形式のデータを POST するだけです。簡単ですね。

```javascript:main.gs
function slackSendTemplate(file) {
  Logger.log('雛型ファイルのリンクを Slack に通知する');

  //スクリプトプロパティを取得する
  const url = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK');

  const params = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify({ text: '雛型ファイルを生成しました ' + file.getUrl() })
  }
  UrlFetchApp.fetch(url, params);  
}
```

初めてテストコード```testSlackSendTemplate```を実行した時に、Google ドライブ操作と、外部サービス接続のアクセス許可を求められます。アクセス許可については、[こちら](https://qiita.com/h-yuki/items/a16fd13602af2039ee43#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E8%A8%B1%E5%8F%AF)を参照して下さい。めでたく実行が完了すると、以下のように Slack にメッセージが表示されます！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/0bea22bd-7134-3a2e-c0a8-9a141c84c6a0.png)

## リッチなメッセージ

[Webhook](https://api.slack.com/incoming-webhooks) の説明ページには、[Attachment](https://api.slack.com/docs/message-attachments) を使ってリッチなメッセージを作成するサンプルがあります。しかし、Attachment の説明ページを見ると、outmoded approach(時代遅れなやり方)なので [Layout Block](https://api.slack.com/messaging/composing/layouts)を使いなさいと書かれています。以下は、Layout Block を使って、テキスト1、水平ライン、ファイルへのリンク、テキスト2 というメッセージを送るコードです。

```javascript:slack.gs
function slackSendSimpleMessage(url, text) {
  const message = { text: text };
  slackSend(url, message);
}

function slackSendRichMessage(url, text1, fileName, fileUrl, text2) {
  const message = {
    blocks: [
      {
        type: 'section',
        text: { type: 'mrkdwn', text: text1 }
      },
      {
        type: 'divider'
      },
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: Utilities.formatString('<%s|_*%s*_>\n%s', fileUrl, fileName, text2)
        }
      }
    ]
  };
  slackSend(url, message);
}

function slackSend(url, message) {
  const params = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(message)
  };
  Logger.log(JSON.stringify(message));
  UrlFetchApp.fetch(url, params);  
}
```

Slack 関連の関数は汎用性が高いので、別のスクリプトファイル ```slack.gs```に分けて定義しました。先ほどの例のシンプルなメッセージを送る関数```slackSendSimpleMessage```と、リッチなメッセージを送る関数```slackSendRichMessage```を定義しています。メッセージ送信部分は共通なので、関数```slackSend```としてくくり出しました。

リッチなメッセージを送信するメインの関数は以下のようになります。

```javascript:main.gs
function slackSendTemplate(file) {
  Logger.log('雛型ファイルのリンクを Slack に通知する');
  const url = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK');
  slackSendRichMessage(url,
                       ':page_with_curl: <!channel> 今週の雛型ファイルを作成しました。',
                       file.getName(), file.getUrl(),
                       ':stars:リンクを開いてダウンロードして下さい。');
}
```

テキストのフォーマットについては、[こちら](https://api.slack.com/messaging/composing/formatting)を参照して下さい。テストコード```testSlackSendTemplate```を実行すると、以下のようにリッチなメッセージが Slack に表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/354068/7f98b408-7fb1-49a9-11b7-e64e593abe7f.png)

# おわりに
実は記事を書いている途中に Attachment でなく Layout Block を使うべきだということに気づいてコードを書き直したので、随分書くのに時間がかかってしまいました。

# おまけ
テキストのフォーマットに関する Slack の[ドキュメント](https://api.slack.com/messaging/composing/formatting)には誤りがあります。[問い合わせページ](https://absclub.slack.com/intl/ja-jp/help/requests/new)から指摘しておいたので、いつか修正されると思いますが、念のためメモしておきます。ちなみに、問い合わせページが日本語のフォームだったので日本語で書いたら、英語でとんちんかんな返事が来ました。英語で説明したら理解して頂けたようでした。

[Automatic parsing](https://api.slack.com/messaging/composing/formatting#automatic-parsing)の説明に以下の記載がありますが、ここの true は false の誤りです。false がデフォルトです。
>You can achieve this in different ways, depending on where the text is being placed:

>- For text in layout blocks set a verbatim attribute of your text objects to **true**. This is actually the default method of processing these text objects.

同じく、少し下の記載の false は true の誤りです。verbatim は「文字どおりに」という意味ですので、true にすると自動パースしてくれなくなるのですね。
>If you want to disable automatic parsing, you have a couple of options depending on the type of text:

>- For text in layout blocks set a verbatim attribute of your text objects to **false**.