SlackBotへのメンションをトリガーにメール送信する方法

# 概要
Slackのスレッドへ勤怠連絡するのにメール報告も行わなければならないのが面倒だったので、Slackへの投稿をトリガーにメール送信する機能を実装しました。

また、メール送信成功時にはSlackBotから特定のチャンネルに投稿させるようにしました。

# Slack Events API
Slackへの投稿やBotへのメンション等の様々なイベントをトリガーに設定して、サーバーにHTTPリクエストを送って様々な機能を実装可能です。

例えば、ほぼリアルタイムにSlackへ投稿したり、Slackの機能に限らずにサーバー上で実現できることは何でも実現させることができます。

# HTTPリクエストを受け取るサーバー
今回は個人的に気になっていたGoogle Apps Script(GAS)を使って実装します。

# 実装手順
## GASでサーバーを用意する
1. Googleドライブにログイン後、新規→その他→Google Apps Scriptの順にクリックする。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/d130dafa-6bc4-aea7-a753-d06f6ca5c8ba.png)
1. Slackのリクエスト先の認証を成功させるために表示されたエディタに下記のコードを記述して保存する(ctl + s)。

```javascript
function doPost(e) {
  const params = JSON.parse(e.postData.getDataAsString());
  return ContentService.createTextOutput(params.challenge);
}
```

　　　　　　　　
3. デプロイ→新しいデプロイの順にクリックする。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/2f76e58b-40b9-e24a-4ee7-1824f1d3d943.png)
4. 歯車マーク→ウェブアプリの順にクリックする。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/4b2fab75-20e7-1765-5714-78a7a33fbb69.png)
5. 説明を簡単に記述し、アクセスできるユーザーを「全員」に設定する(アクセス制限をかけるとSlackからアクセス出来ないため、後ほどSlackが生成するトークンを利用した実行制限を実装する)。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/575ce3cc-5c38-4f56-3cbb-87dbcb843539.png)
6. デプロイ後に表示されるウェブアプリURLはすぐに使うのでコピーしておく。

## slack apiを作成する
1. [slack apiのトップページ](https://api.slack.com/ "slack apiのトップページ")から「Create an app」をクリックする
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/447252ed-f686-6ef3-520a-5f6bc6211205.png)
1. From scratchを選択
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/6c8c6813-2cb3-ea80-b40f-e05516afa8b0.png)
1. App Nameを適当に設定し、アプリケーションを利用するSlackのワークスペースを選択して、「Create App」をクリックする。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/cbf19aa7-24e5-8595-539b-ecfbb4eb8739.png)

## イベントを有効にする
1. Event Subscriptionsをクリックする。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/6df65b83-7fba-1374-28b1-c93b66000da5.png)
1. Enable Eventsを「On」にして、先ほどGASで作成したウェブアプリケーションのURLをRequest URLに貼り付ける。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/322a12f1-e0e1-598d-d1cf-cfa3488b8d2b.png)
1. 「Verified」と表示されれば、ひとまず成功です。「Your URL didn't respond with the value of the challenge parameter.」と表示された場合は今までの工程で間違っている可能性があります。
1. Subscribe to bot events→Add Bot User Eventの順にクリックする。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/72dfe38b-150b-c37b-b68e-15f3431322dc.png)
1. 今回はイベントトリガーはBotのメンションを設定したいので、「app_mention」を選択して「Save Changes」をクリックする。

## 権限を設定する
1. メニューからOAuth & Permissionsを開く。
1. ScopesのBot Token ScopesのAdd an OAuth Scopeを都度クリックして以下の権限を付与する。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/2f9031a9-3e29-4476-e4d8-91cc8f33a6eb.png)
    - app_mentions:read
    - channels:history
    - channels:join
    - chat:write
    - users:read

## Workspaceにインストールする
1. メニューからBasic Informationを開く。
1. Install your appの「Install　to Workspace」をクリックする。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/62519887-3e0f-d89c-6629-b68c59d69c87.png)
1. 表示されたアクセス権限が間違いなければ、「許可する」をクリックする。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/3e789aa7-5a34-90ec-64c8-0efe8c149169.png)

## アクセス制御に利用するトークンをGASのプロパティに設定する
```javascript
function setValueｓ() {
  PropertiesService.getScriptProperties().setProperties({SLACK_ACCESS_TOKEN: 'accessToken',SLACK_VERIFICATION_TOKEN: 'verifyToken'});
};

```
1. 上記のコードをGASのエディタに追加する。
1. slack apiでOAuth & Permissionsを開く。
1. OAuth Tokens for Your WorkspaceのBot User OAuth TokenをaccessTokenの箇所にコピペする。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/4e203632-c3a0-0129-8a84-6dcd84444489.png)
1. slack apiでBasic Informationを開く。
1. App CredentialsのVerification Tokenを上記コードのverifyTokenの箇所にコピペする。
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/a5006cd3-2025-0fb4-e026-37b4e8555f66.png)
1. GASのエディタ上で実行する関数を「setValues」に設定して、実行をクリックする。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/ce1271e3-46d0-7220-80a0-908073675337.png)
1. エラーが発生せず、実行完了した場合は全てのコードを削除して構いません。

## アクセス制御を含むメール送信アプリケーションを作成する
1. GASのエディタに下記のコードをコピペして、自分の利用方法に合わせて設定値や文章を変更する。

        
```javascript
function doPost(e) {
  try{
    if(typeof e.postData === 'undefined') {
      return createTextOutput('invalid request');
    }

    const accessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
    const verificationToken = PropertiesService.getScriptProperties().getProperty("SLACK_VERIFICATION_TOKEN");

    if(e.postData.type === 'application/json') {
      const params = JSON.parse(e.postData.getDataAsString());
      // リクエストURL認証用の処理
      if (typeof params.challenge !== 'undefined') {
        return createTextOutput(params.challenge);
      }
      // 検証用トークンの検証
      if (params.token !== verificationToken) {
        return createTextOutput('invalid request');
      }
      // 特定のチャンネルのみのメンションを受け付ける場合はチャンネルIDを検証
      if (params.event.channel === "ChannelId") {
        const userData = JSON.parse(UrlFetchApp.fetch('https://slack.com/api/users.info?user=' + params.event.user, createRequestOptions('GET', accessToken)).getContentText());

        sendMail(userData.user, params);
        postChatMessage(createRequestOptions('POST', accessToken), userData.user.real_name + 'さんの勤怠連絡をメールしました。');
      }
    }
  }catch(err) {
    return createTextOutput(err);
  }
}

function createTextOutput(params) {
  if (typeof params === 'string') {
    return ContentService.createTextOutput(params);
  } else {
    return ContentService.createTextOutput(JSON.stringify(params));
  }
}

function createRequestOptions(method, accessToken) {
  return {'method': method,
    'headers': {
      'Authorization': 'Bearer ' + accessToken
    }
  }
}

function postChatMessage (options, message) {
  const payload = {
    'channel': '投稿するチャンネル名',
    'text': message,
    'username': '作成したBot名'
  };

  UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', {...options, payload});
}

function sendMail(user, params) {
    const toAdr = "";         //送り先アドレス
    const ccAdr = "";         //Ccアドレス
    const bccAdr = "";        //bccアドレス
    const subject = "";       //メールの題目
    const name = "";          //送り主の名前
    const files = new Array();//添付ファイル
    
    const replaceText = params.event.text.replace(`<@${params.authorizations[0].user_id}>`, '');

    const body =
      'お疲れ様です。' + user.real_name + 'です。\n\n' +
      replaceText + '\n\n' +
      'よろしくお願いいたします。';

    MailApp.sendEmail({to:toAdr, cc:ccAdr, bcc:bccAdr, subject:subject, name:name, body:body, attachments:files});
}
```
※最低限、toAdr及びsubject、channelId、投稿するチャンネル名、作成したBot名は変更してください。
　　　　　　　　
2. デプロイ→デプロイを管理の順にクリックする。
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/aa89ffb1-a105-f663-75a9-85d302906902.png)
3. 鉛筆アイコン(編集ボタン)をクリックした後、バージョンを新バージョンに設定して、デプロイをクリックしてアプリケーションを更新する。
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1716074/3e3680c2-18f9-f4a1-ce73-372f871a03ce.png)
4. 実際にBotに対してメンションしてメール送信テストを実行する。

# 最後に
今回はGASのメソッドを利用してメール送信を実装しましたが、メールサーバーを別途用意したり、SendGrid等を使えば、メールの送信元を指定することも可能です。

GASのメール送信は1日100通の制限があるので、利用頻度や用途に合わせてカスタマイズしていただければ幸いです。