slackで「投稿ルールが守られない問題」を自作のスラッシュコマンドで解決する(GASコード解説編)

# 前置き
[slackで「投稿ルールが守られない問題」を自作のスラッシュコマンドで解決する(設定編)](https://qiita.com/marogoma/items/e3003564c1b8b7b09e29)からの続きになります。

コード全文は[githubリンク](https://github.com/marozex/myGas/blob/master/slash_command/slash.gs)をご確認ください。

## 処理の流れ(再掲)
>slack上でスラッシュコマンドを投稿
→slackAppの「コマンドごとに設定されたURL」にPOST
→GoogleAppsScript(サーバー)が受けてダイアログを返す
→slack上にダイアログが表示され、各項目を入力して投稿をする。
→slackAppの「InteractiveComponentsに設定されたURL」にPOST
→GoogleAppsScript(サーバー)が受けて、入力内容を整形してslackに投稿

GASで処理が行われるのは

- ①GoogleAppsScript(サーバー)が受けてダイアログを返す
- ②GoogleAppsScript(サーバー)が受けて、入力内容を整形してslackに投稿

の２つになりますので、①②それぞれ解説します。

## ①②共通処理
```js
function doPost(e) {
  //GASのプロパティストアに登録したVerification Token
  var verified_token = PropertiesService.getScriptProperties().getProperty('verified_token');
  var verificationToken = e.parameter.token || JSON.parse(e.parameter.payload).token || null;
  if (verificationToken !== verified_token) { // AppのVerification
    console.log(e);
    return ContentService.createTextOutput();
  }
```
トークンによる認証を入れています。
①と②でslackから送られるデータ構造がだいぶ違う(片方はJSON.parse必要)ことに注意しましょう。
今回のbotの機能とは直接関係ないですが、[無職やめ太郎さんのJSの記事](https://qiita.com/Yametaro/items/17f9b2baa67440b8664a)を読んで、

```js
var verificationToken = e.parameter.token ? e.parameter.token : ((e.parameter.payload && JSON.parse(e.parameter.payload).token) ? JSON.parse(e.parameter.payload).token : null);
```
と書いていたのを、

```js
var verificationToken = e.parameter.token || JSON.parse(e.parameter.payload).token || null;
```
とリファクタ出来ました。非常に勉強になりました。

## ①GoogleAppsScript(サーバー)が受けてダイアログを返す

```js
  if (e.parameter.command === '/bihin' && e.parameter.channel_name !== channelName) {
    console.log(e);
    //response_typeをephemeralにすることで実行ユーザーにだけ通知される。
    var rtnjson = {"response_type": "ephemeral","text": "/bihinコマンドは備品管理チャンネルでのみ使用可能です"};
    return ContentService.createTextOutput(JSON.stringify(rtnjson)).setMimeType(ContentService.MimeType.JSON);
```
期待しているチャンネル以外でコマンドが実行された際にエラー文言を表示する処理です。
コード内にコメントで書いていますが `ephemeal` のオプションをつけることで「投稿者だけが見えるメッセージ」をslack上に表示させる事ができます。

```js
  } else if (e.parameter.command === '/bihin') {
    var createdDialog = createDialog(e);
    var options = {
    'method' : 'POST',
    'payload' : createdDialog,
    };
    var slackUrl = "https://slack.com/api/dialog.open";
    var response = UrlFetchApp.fetch(slackUrl, options);
    return ContentService.createTextOutput();
```
正しいチャンネルでコマンドが実行された場合に `createDialog(e)` でダイアログを生成してslackに返します。
(私は `dialog.open` を実行する際にメソッドを `POST` にするのを忘れてハマったのでご注意下さい。。。)

```js
function createDialog(e){
  var trigger_id = e.parameter.trigger_id;
  var token = PropertiesService.getScriptProperties().getProperty('OAuth_token');
  var dialog = {
    "token": token, // OAuth_token
    "trigger_id": trigger_id,
    "dialog": JSON.stringify({
      "callback_id": "irai_dialog",
      "title": "備品購入依頼依頼フォーム",
      "submit_label": "依頼する",
      "elements": [
        {
          "type": "text",
          "label": "品名",
          "name": "name"
        },
        {
          "type": "text",
          "subtype": "url",
          "label": "購入希望品URL",
          "name": "url",
        },
        {
          "type": "select",
          "label": "購入承諾者(上長)",
          "name": "approver",
          "options": [
            {
              "label": "A部長",
              "value": "A部長"
            },
            {
              "label": "B課長",
              "value": "B課長"
            }]
        },
        {
          "type": "text",
          "label": "納品希望日(※希望日無ければ↓は編集しなくてokです)",
          "name": "date",
          "value": "2019/XX/XX"
        },
        {
          "type": "textarea",
          "label": "備考欄",
          "name": "freeText",
          "optional": true, //この指定がないとrequiredになる
          "placeholder": "備考欄のみ入力必須ではありません"
        },
      ]
    })
  };
  return dialog;
}
```
[slack公式のダイアログの説明](https://api.slack.com/dialogs)がかなり充実しているので、そちらを参考にすると良いと思います。
少しだけ役立つかもしれない情報を箇条書きで記しておきます。

- elementsとして配置できる要素の数に上限がある(10個。つい最近まで5個だったような・・・)
- どの入力欄もデフォルトで入力必須(`"optional": true`で必須解除)
- ユーザーに見せずに情報を保持させるためのプロパティとして `state` がある
- `subtype` を使うとスマホ経由での入力が楽になる(例：`"subtype":"tel"` とすれば数字入力が自動で選ばれる)

## ②GoogleAppsScript(サーバー)が受けて、入力内容を整形してslackに投稿
```js
  } else {
    var p = JSON.parse(e.parameter.payload)
    var postChannel = p.channel.id;
    var s = p.submission;
    var free = s.freeText || "なし" 
    var test = slackApp.postMessage(postChannel,
                         "購入依頼by <@" + p.user.id + ">\n【購入品名】　：" + s.name
                         +"\n【購入URL】　：" + s.url +"\n【購入承諾者】：" + s.approver 
                         + "\n【納品希望日】：" + s.date  +"\n【備考】　　　：" + free,
                         {"username" : "備品依頼bot", "icon_emoji" : ":hammer_and_wrench:"});
    
    return ContentService.createTextOutput();//←これが無いとslackのダイアログが閉じない。
  }
```
ここで実行される処理は、整形してslackに投稿するだけです。
コメントで書いていますが `postMessage()` をしただけではダイアログ自体が閉じないので、 `ContentService.createTextOutput()` という処理を最後に入れましょう。


コード解説は以上となります。ありがとうございました。