社内チャットの会話をWatsonで感情分析し、トラブルを未然に防ぐ。

[IBM Cloud Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ibmcloud) 12月16日分です。

# 序文
Slackのようなツールを、社内のサーバールーム内のサーバーや、自社用にリソース専有させたクラウド環境等で使いたいというニーズは常にあります。気づけば10年以上グループウェアの世界に、提案側、開発側、導入側、ユーザー側として携わり、ヘルプデスク組織の運営などもやってきました。新卒一年目にPHPでWikiシステムを一から作っていたなぁ...とか思い出はあります。

社外からの問い合わせやSNSへの対応は今後のネタとして残しておくとして、社内での会話からトラブルにならないように、ネガティブコメントを注視しておく必要があります。特にお客様とのトラブルに発展しないようにフォローできる仕組みを、IBM Cloudで使用可能なWatsonを用いて構築してみましょう。

# 準備
下記のような環境を用意します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/a1932e60-47da-a1bf-3a2f-91032de9c9e3.png)

## 社内チャットシステムとして、Mattermostサーバーを用意します。
構築自体は、公式のオンラインドキュメント通りに実施しています。DBのパスワード等は変えます。
使用したMattermostは、無料で使えるOpen Source Team Editionで、バージョン5.6です。

- [公式のインストール手順(Ubuntu 16.04版)](https://docs.mattermost.com/install/install-ubuntu-1604.html)
- [Mattermost ダウンロード](https://mattermost.com/download/)

### 外向きのWebhookを設定します。
Mattermost内に投稿先としてチームを作成し、チーム作成後「統合機能」の画面から、「外向きのWebhook」の設定を行います。

サンプルとして、トリガーワードは#helpとしています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/cacf2a84-b684-be29-6abb-d74ef92fe8f3.png)

Mattermostにおける「外向きのWebhook」については、以前書いた記事がありますので、興味がありましたらご確認ください。
参考資料：[Mattermost向けチャットボットをNode-REDとWatson Assistantで実装](https://qiita.com/kolinz/items/1a6669646573cfdb4b17)

## タスク管理システムとして、Jira Software環境を用意します。
下記の手順を用いて構築しました。もちろん、ここではIBM Cloud IaaS上に構築していますが、自社のプライベートクラウドや社内のサーバールームでも構いません。後述のNode-RED環境から接続できれば良いです。
構築手順：[IBM Cloudを用いてJIRA Softwareで、日々のタスク管理をはじめる。](https://qiita.com/kolinz/items/039e8b7b47a358ff0c86)

Jiraに登録するタスクは、「課題」と言います。Redmineではチケットと言いますし、CRMなどでは、ToDoと言います。言い方の違いで意味は同じです。

### カスタムフィールド作成
Jiraには感情分析した値を格納する項目がないため、感情分析した結果として「怒り」「嫌悪」「恐れ」「楽しみ」の4つの感情スコアの値を格納する追加項目、カスタムフィールドを作成します。また作成したカスタムフィールドIDを確認しておきます。
後述のサンプルコードでは、次のように設定しています。

| カスタムフィールド名 | フィールドタイプ | カスタムフィールドID | Node-REDの「POST - Create Issue」の記述 |
|:-----------------|:------------------|:------------------|:------------------|
| 怒り             | 数値フィールド | 10206 | "customfield_10206" |
| 嫌悪           | 数値フィールド | 10207 | "customfield_10207" |
| 恐れ             | 数値フィールド | 10208 | "customfield_10208" |
| 楽しみ             | 数値フィールド | 10209 | "customfield_10209" |

## MattermostとJira Softwareのデータをつなぐために、Node-REDサーバーを用意します。
MattermostとJira Softwareの両方に接続できる環境に、Node-REDサーバーを構築します。MattermostとJira Softwaareの両方に接続できれば、パブリッククラウドやプライベートクラウド、社内のサーバールームでも構いません。Node-REDのインストールには、公式のオンラインドキュメントを使用しています。

- [公式のインストール手順](https://nodered.org/docs/getting-started/installation)


# Node-REDで実装

## Node-REDに追加したノード
サーバーにNode-REDをインストールした後、標準では付属しないWatsonやJiraに接続するノードを追加します。
node-red-contrib-jira:https://flows.nodered.org/node/node-red-contrib-jira
node-red-node-watson:https://flows.nodered.org/node/node-red-node-watson

## フロー図
![Mattermostに投稿されたメッセージを感情分析しJiraに課題登録するフロー.PNG](https://qiita-image-store.s3.amazonaws.com/0/41414/f99839e7-e4d1-05db-69b9-3ee000274264.png)

## 処理の流れ
1. Mattermostに投稿されたトリガーワード付きのメッセージをNode-REDで受信
2. Node-REDで受信したメッセージを「Watson Language Translator」で日本語から英語に翻訳。翻訳する理由は、感情分析を行う「Watson Tone Analyzer」が2018年12月現在日本語未対応のため、英語に翻訳します。
3. 「Watson Tone Analyzer」で感情分析します。
4. 感情分析の結果、怒りのスコアが「0.05」以上の時に、タスク管理システムの「Jira」に課題として登録するためのJSON形式のデータを作成します。
5. 作成したJSON形式のデータを「Jira」に渡し、要対応のネガティブコメントとして新規課題登録します。 

## サンプルコード
下記のサンプルコードをNode-REDで読み込み、「language translator」ノードや「tone analyzer」ノード、「jira-issue-create」ノードをそれぞれダブルクリックし設定します。

```
[{"id":"fa32047e.c96898","type":"tab","label":"感情分析によるタスク管理","disabled":false,"info":""},{"id":"d1690281.96fc","type":"http in","z":"fa32047e.c96898","name":"Mattermost POST","url":"/mmpost","method":"post","upload":false,"swaggerDoc":"","x":130,"y":40,"wires":[["272aba56.17d726"]]},{"id":"dcdc3ecc.d75f2","type":"debug","z":"fa32047e.c96898","name":"debug:Mattermost Imcoming Webhook","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":670,"y":40,"wires":[]},{"id":"272aba56.17d726","type":"function","z":"fa32047e.c96898","name":"Cut Trigger Word","func":"var message = msg.payload.text;\nvar cutmsg = message.slice(5);\nmsg.payload = cutmsg;\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":40,"wires":[["dcdc3ecc.d75f2","2f8329a9.bc1e56","ebe4c8a3.3ca2d8"]]},{"id":"2f8329a9.bc1e56","type":"watson-translator","z":"fa32047e.c96898","name":"","action":"translate","basemodel":"en-tr","domain":"general","srclang":"ja","destlang":"en","password":"","apikey":"","custom":"","domainhidden":"general","srclanghidden":"ja","destlanghidden":"en","basemodelhidden":"","customhidden":"","filetype":"forcedglossary","trainid":"","lgparams2":true,"neural":false,"default-endpoint":true,"service-endpoint":"https://gateway.watsonplatform.net/language-translator/api","x":150,"y":160,"wires":[["7e04a1f.8bdb26"]]},{"id":"7e04a1f.8bdb26","type":"watson-tone-analyzer-v3","z":"fa32047e.c96898","name":"","tones":"emotion","sentences":"true","contentType":"false","tone-method":"generalTone","interface-version":"2016-05-19","inputlang":"en","default-endpoint":false,"service-endpoint":"https://gateway-tok.watsonplatform.net/tone-analyzer/api","x":380,"y":160,"wires":[["32339235.6f97ce","27c3ad11.5e6372"]]},{"id":"32339235.6f97ce","type":"debug","z":"fa32047e.c96898","name":"debug:感情分析","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":600,"y":160,"wires":[]},{"id":"9db37b51.23bfb8","type":"jira-issue-create","z":"fa32047e.c96898","name":"","server":"667cbe9c.e4b3e","x":600,"y":240,"wires":[["cc2c73ae.2807d"]]},{"id":"27c3ad11.5e6372","type":"switch","z":"fa32047e.c96898","name":"","property":"response.document_tone.tone_categories[0].tones[0].score","propertyType":"msg","rules":[{"t":"gte","v":"0.05","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":130,"y":247,"wires":[["f132ac7a.3a2d1"],[]]},{"id":"ebe4c8a3.3ca2d8","type":"function","z":"fa32047e.c96898","name":"variable:issue subject","func":"var message = msg.payload;\nvar msg = {\n    note: message\n    };\n    flow.set('desc',msg);\nreturn msg;","outputs":1,"noerr":0,"x":620,"y":100,"wires":[[]]},{"id":"f132ac7a.3a2d1","type":"function","z":"fa32047e.c96898","name":"POST - Create Issue","func":"var desc         = flow.get('desc');\nvar note         = desc.note;\nvar angerscore   = msg.response.document_tone.tone_categories[0].tones[0].score;\nvar disgustscore = msg.response.document_tone.tone_categories[0].tones[1].score;\nvar fearscore    = msg.response.document_tone.tone_categories[0].tones[2].score;\nvar joyscore     = msg.response.document_tone.tone_categories[0].tones[3].score;\nmsg.payload  = {\n        \"fields\": {\n            \"project\":{\"key\":\"EMOTION\"},\n            \"summary\":\"ネガティブコメントを確認\",\n            \"description\":note,\n            \"issuetype\":{\"id\": \"10002\"},\n            \"customfield_10206\":angerscore,\n            \"customfield_10207\":disgustscore,\n            \"customfield_10208\":fearscore,\n            \"customfield_10209\":joyscore\n        }\n    }\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":240,"wires":[["9db37b51.23bfb8"]]},{"id":"cc2c73ae.2807d","type":"debug","z":"fa32047e.c96898","name":"debug:Jira Create Issue","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":630,"y":300,"wires":[]},{"id":"667cbe9c.e4b3e","type":"jira-server","z":"","url":"http://jira-dev.kolinzlabs.com:8080/jira/rest/api/latest/","name":"Jira Server"}]
```

### language translatorノードの設定
IBM Cloudにアクセスし、Watson Language TranslatorのAPI鍵とエンドポイントURLを確認し設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/8f5c44d2-f6ca-acb5-24b4-cac58373510d.png)

### tone analyzerノードの設定
IBM Cloudにアクセスし、Watson Tone AnalyzerのAPI鍵とエンドポイントURLを確認し設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/4c7ad2ed-080e-c759-52fc-bdb64a0b3df9.png)

### jira-issue-createノードの設定
「jira-issue-create」ノードの編集画面で、Jira Softwaare用APIへの接続設定を行います。のドキュメントに無いため接続設定におけるURLは、お使いのJira Software環境におけるJira REST APIのURLになります。

```
https://your-jira-url/rest/api/latest/
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/69ac8c5b-8d35-e506-6fa7-5194b1db24a3.png)

#動作確認
Mattermostにトリガーワード(サンプルコードでは、#help)をつけて、お客様から言われそうなメッセージを投稿します。Watson Tone Analyzerにより感情分析され、怒りや嫌悪といったネガティブコメントと判断された場合、Jira Softwareに新規課題として登録されることを確認できました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41414/e1ef5644-54c8-e09e-c922-204c5ec76a5e.png)

#まとめ
Node-REDを使用することで、社内チャットシステムのMattermostとタスク管理システムのJira Softwareを繋げて、ネガティブコメントがあればフォロー用のタスク(課題)を自動登録する動きを確認することができました。この仕組は、Mattermostを外部向けのSNSに変更することで、マーケティング活動や広報業務にも応用できるかもしれません。(次回のネタにします。)