Webhookを利用して色々なチャットツールにメッセージを送るJavaScriptコードサンプルまとめ


# はじめに
最近、様々なチャットツールがありますね。
（多機能なのでチャットツールという表現には不適切かもしれませんが）

JavaScriptでチャットにメッセージを送るプログラムかいていたので、スニペットとして残すために書いてきたソースコードからピックアップしました。
仕事関係なく「とりあえず動かして試すぞ！遊ぶぞ！」といった気持ちでつくってきたものなので、コードの汚さはご容赦ください。

##今回の対象のチャットツール

- Teams
- Chatwork
- slack
- discord
- LINE Notify

TeamsとChatworkは会社で利用していたので無料で試せて（試させてもらって）いますが、
他のは基本的に無料で好きに試せますのでおすすめ。

## 準備
- WindowsでもMacでもLinuxでも、Nodeが準備できていれば
- Node.js 10.16.x or 12.16.x で確認

# Teamsのチャネルに自作プログラムから飛ばす。
１つ目の紹介なのでちょっとだけ丁寧に説明します。

## 流れ
１. 送りたいチャネルを用意（既存のチャネルでもOK）
２. チャネルにWebhookコネクタを追加
３. コーディング準備
４． Teamsのプログラムを用意
５． つくったら実行

## 実装

### １． 送りたいチャネルを用意（既存のチャネルでもOK）
アカウント作成とかそのあたりは割愛。ちなみにプライベートチャネルでも大丈夫ですので試す場合はまず鍵かけたところがよいかも。

### ２． チャネルにWebhookコネクタを追加
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/c29c6586-9760-a96d-ff82-38bf6311516b.png"><img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/0ca0d449-dd2b-97de-511c-15308b4f40a6.png">
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/27413b32-386b-4445-1f14-d089284eeb67.png"><img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/ce6178f2-d037-9b2a-27fe-68c2bc882ebf.png">


こんな風に作成されます

    https://outlook.office.com/webhook/xxxxxxxxxxxxxxxxxxxx@0xxxxxxxxxxxxxxxxxxxxxxxxxx/IncomingWebhook/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


### ３． コーディング準備
下のコマンドをうって準備します(0からコードを書く場合)

```
> git init
略
> npm init
略
> npm install --save axios
略
```

### ４． Teamsのプログラムを用意

```
    PS C:\work\repository\aocm\samples\chat-webhooks> tree /F
C:.
│  .gitignore
│  index.js
│  package-lock.json
│  package.json
│  teams.js
│
└─node_modules
    ├─axios
```

```text:.gitignore
node_modules
```
git管理する必要ないので除外

```javascript:teams.js

const axios = require('axios');
/**
 * Teamsに通知する
 * @param {*} message 投稿するメッセージ
 * @param {*} teams_url Incoming WebhookのURL
 */
exports.teamsPost = (message, teams_url)=>{
    axios(
        {
        method: 'post',
        url: teams_url,
        headers: {
            'Content-Type': 'application/json',
        },
        data: {
            "type": "message",
            "text": message
        },
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
        console.log('err:', error);
    });

}
```

```javascript:index.js
const teamsUrl = さっきのURL
teamsPost("test", teamsUrl)
```

### ５． つくったら実行

```
> node .\index.js
```

(※これはWindowsでためしてたのでPSのコマンド)

想定したチャネルに届いてればOK!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/fae2c2da-e710-d588-c592-75991334512f.png)

# Chatworkに通知する

※tokenの取得はちょっと特殊なので割愛

https://developer.chatwork.com/ja/endpoint_rooms.html#GET-rooms
## 流れ
１． 通知するには、チャットワークのtokenとroom_idが必要。
2. とりあえずroom_idわからないので新しく部屋をつくりつつ、一覧を取得
3. room_idを特定したらpost

## 実装（ざっくりしたメモ）
リファクタ時にクラスつくるので、一旦てきとうな名前で。

```javascript
exports.chatworkGetRooms=(token)=>{
    try{
    const url ="https://api.chatwork.com/v2/rooms"
    axios(
        {
        method: 'get',
        url,
        headers: {
            'Content-Type': 'application/json',
            'X-ChatWorkToken': token
        }
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
        console.log(error)
        const {
        status,
        statusText
        } = error.response;
        console.log(`Error! HTTP Status: ${status} ${statusText}`);
    });
    }catch(e){
    console.log(e)
    }
}
```

（全部出てきちゃうので抜粋）

```
  {
    room_id: xxxxxxxxx,
    name: 'sample_20201130',
    type: 'group',
    role: 'admin',
    sticky: false,
    unread_num: 0,
    mention_num: 0,
    mytask_num: 0,
    message_num: 1,
    file_num: 0,
    task_num: 0,
    icon_path: 'https://appdata.chatwork.com/icon/ico_group.png',
    last_update_time: 1606694822
  },
```

Postするコードの準備

```javascript
exports.chatworkPost = (room_id, token, message)=>{
    const url = `https://api.chatwork.com/v2/rooms/${room_id}/messages`
    console.log(url)
    axios(
        {
        method: 'post',
        url,
        headers: {
            'X-ChatWorkToken': token
        },
        data: `body=${message}`
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
        console.log(error)
    });
}
```

さっきのtokenを渡すようにして実行すればとびます。

ちなみにテキストをChatwork固有の書き方をすれば、送信後にちゃんと反映されます。
https://developer.chatwork.com/ja/messagenotation.html

# Slackに通知する
## 流れ
いっぱい参考記事あるので割愛

## 実装（ざっくりしたメモ）
slackbotとチャンネルとtokenを用意して、通知したいメッセージをつめて実行すればとびます。
（package.jsonは割愛）

```javascript
/**
 * slack にメッセージをpostする
 */
exports.slackMessage = (message="test!")=>{
  try{
    console.log(message)
    const querystring = require('querystring');
    const url = 'https://slack.com/api/chat.postMessage'
    const token = process.env.slack_token||"non_token"
    const text = message
    const channel = "#"+ process.env.slack_channel||"non_ch"
    console.log(channel)
    if(token==="non_token") throw new Error("slack bot tokenがありません")
    if(token==="non_ch") throw new Error("チャンネルを指定してください")
    axios(
      {
        method: 'post',
        url,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        data: querystring.stringify({
          text,
          token,
          channel
        }),
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
      console.log(error)
      const {
        status,
        statusText
      } = error.response;
      console.log(`Error! HTTP Status: ${status} ${statusText}`);
    });

  }catch(e){
    console.log(e)
  }
}

```

# Discordに、他のAPIから取得したデータをフォーマットして通知

## 概要
https://github.com/aki-lua87/PSO2API
こちらの方が公開しているAPIからデータをもらってDiscordに送るプログラムを書きます。
この場を借りてお礼申し上げます。本当にありがとうございます。

**この記事を参考にする場合はAPI作成者に迷惑にならないようにおねがいします**

## 流れ
1. 通知したいサーバーにWebhookの設定をして、urlを取得
2. コードをかいてAPIからデータを取得する
3. そのデータを整形してurlにpostする
4. せっかくなのでcronに仕込んで毎日おくる

## 実装（ざっくりしたメモ）
discordの方をちょいちょいっと準備して、、、
<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/c14bd760-b493-fb6e-6e46-a9ade9c5de01.png"><img width="500" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/ff0f9c57-152e-8478-5b67-2541f8a796fd.png">


今回はざつに１つのファイルにコーディングします。
APIコールして取得したデータを整形してDiscordのWebhookURLにPostします。
（1時間くらいでさらっと書いたのでインデント一部崩れている、リファクタ前のものです）

```javascript:index.js
const axios = require('axios');

try{
    const url = "https://pso2.akakitune87.net/api/emergency"
    const today= getNowYMD()
    axios(
    {
        method: 'post',
        url,
        headers: {
            'Content-Type': 'application/json',
        },
        data: {
            "EventDate":today,
            "EventType":"緊急"
        },
    }).then(response => {
        const webHookURL = 略
        discord(webHookURL, apiToMessage(response.data, today))
    }).catch(error => {
    console.log(error)
    const {
        status,
        statusText
    } = error.response;
    console.log(`Error! HTTP Status: ${status} ${statusText}`);
    });
}catch(e){
    console.log(e)
}

function discord(webHookURL, message){
    try{
    axios(
        {
        method: 'post',
        url:webHookURL,
        headers: {
            'Content-Type': 'application/json',
        },
        data: {
            "content":message
        },
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
        const {
            status,
            statusText
        } = error.response;
        console.log(`Error! HTTP Status: ${status} ${statusText}`);
    });
    
    }catch(e){
        console.log(e)
    }
}

function getNowYMD(){
    let dt = new Date();
    let y = dt.getFullYear();
    let m = ("00" + (dt.getMonth()+1)).slice(-2);
    let d = ("00" + dt.getDate()).slice(-2);
    let result = y + m + d;
    return result;
}
function apiToMessage(array, date){
    let message = date + "\n"
    for (let i = 0; i<array.length; i++){
    message = message + format(array[i])
    }
    return message
}
function format(object){
    let text = object.Hour + "時: " 
    text = text + object.EventName + "\n"
    return text
}

```

コードをかいて試したら、つけっぱなしのiMacのcrontabにセットしてます。

```cron
$ crontab -l
## pso2 
3 0 * * * ~/.anyenv/envs/nodenv/shims/node ~/repository/aocm/pso2api-to-discord/index.js >> ~/repository/aocm/pso2api-to-discord/cron.log
```

<img width="500" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/2ef7f765-9c45-f965-14b9-46b8fea7aef0.png">


# LINE Notify
LINE NotifyはLINEの中でも非常にシンプルに通知を飛ばせるので、ちょっと通知で遊びたいというときにおすすめです。
チャットボットとかもいつか作ってみたいですけどやってないです。
https://notify-bot.line.me/ja/

LINE Notifyについては、母向けに「図書館のサイトにPuppeteerでログインしてスクレイピングして、予約してた本が受け取れる状態になっていたらLINEで通知する」というプログラムの一部として利用しました。

仕事で身に付けたことで親孝行するの、ちょっと自分の成長を感じられていいです。 ~~9割自己満ですけども。~~

## 流れ
1. LINEのアカウントでhttps://notify-bot.line.me/ja/ にログインしてNotifyを有効化
2. アクセストークンの発行・通知するNotifyと母とぼくだけが参加するトークルームを作成
3. プログラムを作る
4. tokenとプログラムをcrontabでセットして毎朝実行する

## 実装（ざっくりしたメモ）
呼び出しコードは割愛。
スクレイピングの方は割愛しまして、通知部分だけ転記します。

```javascript
const token = process.env.token;

exports.lineNotice = (message="test!")=>{
  try{
    console.log(message)
    const axios = require('axios');
    const querystring = require('querystring');
    const text = message

    axios(
      {
        method: 'post',
        url: 'https://notify-api.line.me/api/notify',
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        data: querystring.stringify({
          message: text,
        }),
    }).then(response => {
        console.log('res:', response.data);
    }).catch(error => {
      console.log(error)
      const {
        status,
        statusText
      } = error.response;
      console.log(`Error! HTTP Status: ${status} ${statusText}`);
    });

  }catch(e){
    console.log(e)
  }
}
```

crontab割愛。こんな感じにとんできます。
<img width="500" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417134/fea2fa8f-0786-38d4-a04d-ebb136531245.png">

# おわりに
私生活に使えるプログラムかくのは楽しいです。

余談ですが、今回はMacだったりWindowsだったりDockerコンテナだったりとにかくローカル実行しているので、そのうちAWSLambdaとかに組み込んでみようかなと思います。
チームで管理するときとかはローカルで管理したら属人的で良くないですしね。
あと・・・見直してみると、eslintでめっちゃ怒られそうなプログラム並んでますね。お目汚し失礼しました。
いつか整理できたらGithubでも公開します。