NuxtとNode.jsサーバーでSlack通知実装

##設計
- NuxtベースアプリケーションからSlackに通知する機能を実装したい。
- 定期的な自動再送機能もほしい。
- トークンをNuxt側に置くのはちょっと...

そんな要件を満たすためには、Nuxtからサーバーを経由するのもありかなという感じで作成。

##必要なもの
1. Nuxtアプリケーション
2. Node.js (当方Version 13.2.0)
3. 設定済みSlack API (トークンとBot Token Scopesにてchat:write権限が必要。#generalに参加済み）

##Nuxtでの実装
求められるものは
1. メッセージ入力
2. 宛先入力
3. 再送間隔 or 再送しないの入力
4. 設定内容のPOST

とりあえず上記の4つを実装すれば最低限機能は満たせそうなので書いていく。
見た目はこの際捨て置く。

またquerystringを使っているので未インストールなら

~~~
npm install --save querystring
~~~

以下、コード。

```vue
<template>
  <div class="create-post">
    <input type="text" v-model="message" id="message">
    <input type="text" v-model="channel" id="channel">
    <input type="text" v-model="schedule" id="schedule">
    <button v-on:click="send">
      送信
    </button>
  </div>
</template>

<script>
  import querystring from "querystring"
  export default {
    data() {
      return {
        message: '',
        channel: '',
        schedule: 'none'
      }
    },
    methods : {
      async send(){
        let json_data = {
          message: this.$data.message,
          channel: this.$data.channel,
          schedule: this.$data.schedule
        };
        const response = await this.$axios.$post('/test/rest', querystring.stringify(json_data));
      }
    }
  }
</script>

<style scoped>
  .create-post{
    width: 30%;
    height: 200px;
    padding-left: 200px;
  }
  #message{
    margin-top: 10%;
  }
  #channel{
    margin-top: 10%;
  }
  #schedule{
    margin-top: 10%;
  }
</style>
```

そうするとこのようなページが出来上がる

<img width="863" alt="スクリーンショット 2020-02-25 17.23.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319076/6317d244-a646-2354-8606-44c680ffbb0e.png">

今回、NuxtからサーバーにPOSTするために[@nuxtjs/axios](https://axios.nuxtjs.org/)を使用している。
詳しくはご自分で調べてほしいのだが、使用する際、nuxt.confing.jsにて以下の文を編集・追加している。


```javascript
  modules: [
    '@nuxtjs/axios',
  ],
  axios: {
    proxy: true
  },
  proxy: {
    '/test/': {
      target: 'http://0.0.0.0:3000/',
      pathRewrite: {'^/test/': ''}
    }
  },
```


このように設定することでCORSを簡単に解決することができる。
[クロスオリジンリソースを共有するには？](https://ja.nuxtjs.org/faq/http-proxy/)

##サーバーの用意

サーバーでは次のような処理を行いたい。
1. HTTP Requestを受けとる
2. 中身のデータからメッセージとあて先、定期再送情報を取り出す
3. それに合わせてSlackに通知する

それではコードを書いていく。
これにあたって、以下の記事やサイトを参考にさせていただいた。
[Nodejs HTTP/HTTPS サーバーとクライアントのコーディングパターン](https://qiita.com/MahoTakara/items/64706cdad0d920cf1a1a)
[【Node.js】Slack APIを使用してメッセージを送信する](https://qiita.com/kou_pg_0131/items/31728a4b7b7133198207)
[Node.jsで定期実行メモ](https://qiita.com/n0bisuke/items/66abf6ca1c12f495aa04)
[node-cron npm](https://www.npmjs.com/package/node-cron)
[node-fetch npm](https://www.npmjs.com/package/node-fetch)


```javascript
const http = require('http');
const fetch = require('node-fetch');
const cron = require('node-cron');
const queryString = require("querystring");
const StringDecoder = require("string_decoder").StringDecoder;
const Decoder = new StringDecoder('utf8');

let server = http.createServer();

async function postToSlack(token, msg, channel) {
    const { WebClient } = require('@slack/web-api');
    const client = new WebClient(token);
    const params = {
        channel: channel,
        text: msg
    };

    await client.chat.postMessage(params);
}

let tasks = [];
server.on('request', function (req, res) {
    req.on('data', async function(chunk) {
        let json = JSON.parse(JSON.stringify(queryString.parse(Decoder.write(chunk))));

        if (json.schedule === 'none') { //スケジュール指定がない場合
            await postToSlack('your token here', json.message, json.channel);
        } else { //ある場合
            let task = cron.schedule(json.schedule, async () => {await postToSlack('your token here', json.message, json.channel);});
            tasks.push(['start', task]);
        }

        let return_json = {
            state: true,
        };

        res.writeHead(200, {'Content-Type': 'application/json'});
        let replyData = queryString.stringify(return_json);
        res.write(replyData);
        res.end();
    });
});

server.listen(3000);
```


##実際に試す
Nuxtのページで文章とあて先を入力して送信。

<img width="786" alt="スクリーンショット 2020-02-25 18.23.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319076/5c49b335-0c2b-d694-eeb7-e18d68660559.png">

そしてSlackに通知が来た。

<img width="397" alt="スクリーンショット 2020-02-25 18.26.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319076/ee08945f-bc2e-1eb2-11fa-d51e0adbbe97.png">


定期実行も確認

<img width="755" alt="スクリーンショット 2020-02-25 18.30.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319076/1437749d-b4ca-e24e-bec1-eb510ec69523.png">
<img width="375" alt="スクリーンショット 2020-02-25 18.31.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319076/59d8bed4-a94b-191c-b50e-06e9fbc5a3cf.png">


以上、実装完了とす。

##感想
エラー処理を全く実装していないので怖い。
APIが用意されていると楽。
あとはSlackに限らず通知できるように拡張できそう。

##おわりに
お読みいただきありがとうございました。なにか間違いがございましたらコメントへよろしくお願いします。