急性中耳炎診断支援LINE Botを改良しHerokuにデプロイ

##概要
プログラムの勉強を始めて4か月ほどの開業医です。

以前、忙しい臨床現場でも診療ガイドラインに沿った治療を簡単に行えるようにするため、医療者向けに[急性中耳炎の重症度と推奨治療が簡単に分かるLINE Bot](https://qiita.com/doikatsuyuki/items/8ded5f3a5d4e6e2eaa4b)を作成しました。

以前のものは初診の患者さんに対して重症度判定と推奨治療の提示を行うものだったのですが、今回は再診時の推奨治療を提示する機能を追加しました。

さらに、LINE Developersのリッチメニューを使用しUIを向上させ、Herokuにデプロイしました。今回も医療者向けのBotになっています。


##実装
小児急性中耳炎診療ガイドライン（2018年　日本耳科学会）に沿った診療を支援するLINE Bot。
①初診時LINE上で質問に答えていくとスコアリングが行われ、急性中耳炎の重症度と推奨治療が提示される。
②再診時は前回行われた治療を選択すると今回の推奨治療が提示される。

##概念図
node.jsでBot開発しLINE bot APIと連携。Herokuにデプロイ。
<img width="480" alt="heruku nodeja line.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453374/8c4536ab-07f9-4caf-8167-ce8242839140.jpeg">

##動画
リッチメニューから選択ができ、クイックリプライボタンで表示されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">中耳炎Bot リッチメニュー改良版 <a href="https://t.co/yPCpdlJmmj">pic.twitter.com/yPCpdlJmmj</a></p>&mdash; 病気のセルフチェック (@Selfcheckhealt1) <a href="https://twitter.com/Selfcheckhealt1/status/1197368297605349376?ref_src=twsrc%5Etfw">November 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 
##作成方法
**1. Botアカウントの作成**

**2. Node.jsでBot開発**

**3. ngrokでトンネリング**

上記の1～3を以下の参考記事の通りに行います。
[1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 ](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)

**4. プログラム作成**

コードを以下のように書き換えます。
LINE Developersの**クリックリプライ**を使用しています。
[LINE Developers](https://developers.line.biz/ja/reference/messaging-api/?fbclid=IwAR2DtZhpwt5RTYMnWfpnCOkhsXWbBBt5Dw3qTIOazY-rjb1sowRzcTkQyW8#confirm
)

```js
'use strict';

// モジュールのインポート
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
const app = express();

// パラメータ設定
const config = {
  channelSecret: '自分のchannelSecret',
  channelAccessToken: '自分のchannelAccessToken'
};

// Webサーバー設定
app.listen(PORT);

// APIコールのためのクライアントインスタンスを作成
const client = new line.Client(config);

// ルーター設定
app.post('/bot/webhook', line.middleware(config), (req, res, next) => {
  res.sendStatus(200);
  console.log(req.body.events);  
  
  Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  // LINE botのプログラム 
  let message = event.message.text;
  let question = "";
  let label1, label2, label3 = "";
  let text1, text2, text3 = "";
  
  let [mes, currentScore] = message.split(":");  
 
  if (message == "初診") {    
    currentScore = 0;
    question = "2歳未満ですか？";
    label1 = "いいえ";//0
    text1 = "2歳以上 トータルスコア:"+String(currentScore);
    message = text1;
    label2 = "はい";//3
    text2 = "2歳未満 トータルスコア:"+String(currentScore+3);
    message = text2;
    label3 = "選択できません";
    text3= "開始";
    message = text3;
  } else if (mes == "2歳以上 トータルスコア"|| mes==  "2歳未満 トータルスコア") {
    question = "耳は痛いですか？";
    label1 = "いいえ";//0
    text1 = "耳は痛くない トータルスコア:"+String(currentScore);
    message = text1;
    label2 = "痛い、痛かった";//1
    text2 = "耳が痛い、痛かった トータルスコア:" + String(Number(currentScore)+ 1);
    message = text2;
    label3 = "とても痛いのが続いている";//2
    text3= "耳がとても痛いのが続いている トータルスコア:" + String(Number(currentScore)+2);
    message = text3;
  } else if (mes == "耳は痛くない トータルスコア"||mes=="耳が痛い、痛かった トータルスコア" ||mes== "耳がとても痛いのが続いている トータルスコア" ) {
    question = "熱はありますか？";
    label1 = "37.5未満";//0
    text1 = "37.5未満 トータルスコア:" + String(currentScore);
    message = text1;
    label2 = "38.5未満";//1
    text2 = "38.5未満 トータルスコア:" + String(Number(currentScore)+1);
    message = text2;
    label3 = "38.5以上";//2
    text3= "38.5以上 トータルスコア:" + String(Number(currentScore)+2);
    message = text3;
  } else if (mes == "37.5未満 トータルスコア"||mes== "38.5未満 トータルスコア"||mes== "38.5以上 トータルスコア") {
    question = "泣いたり、機嫌が悪いですか？";
    label1 = "いいえ"//0
    text1 = "機嫌は悪くない トータルスコア:" + String(currentScore);
    message = text1;
    label2 = "はい";//1
    text2 = "泣いたり、機嫌が悪い トータルスコア:" + String(Number(currentScore)+1);
    message = text2;
    label3 = "選択できません";
    text3= "開始";
    message = text3;
  } else if (mes == "機嫌は悪くない トータルスコア" ||mes== "泣いたり、機嫌が悪い トータルスコア" ) {
    question = "鼓膜が赤いですか？";
    label1 = "いいえ";//0
    text1 = "鼓膜は赤くない トータルスコア:" + String(currentScore);
    message = text1;
    label2 = "一部だけ赤い";//2
    text2 = "一部だけ赤い トータルスコア:" + String(Number(currentScore)+2);
    message = text2;
    label3 = "全体に赤い";//4
    text3= "全体に赤い トータルスコア:" + String(Number(currentScore)+4);
    message = text3;
  } else if (mes ==  "鼓膜は赤くない トータルスコア" ||mes== "一部だけ赤い トータルスコア" ||mes== "全体に赤い トータルスコア") {
    question = "鼓膜は腫れていますか？";
    label1 = "いいえ";//0
    text1 = "鼓膜は腫れていない トータルスコア:" + String(currentScore);
    message = text1;
    label2 = "一部だけ腫れている";//4
    text2 = "一部だけ腫れている トータルスコア:" + String(Number(currentScore)+4);
    message = text2;
    label3 = "全体に腫れている";//8
    text3= "全体に腫れている トータルスコア:" + String(Number(currentScore)+8);
    message = text3;
  } else if (mes == "鼓膜は腫れていない トータルスコア"||mes=="一部だけ腫れている トータルスコア" ||mes== "全体に腫れている トータルスコア" ) {
    question = "耳垂れは出ていますか？";
    label1 = "いいえ";//0
    text1 = "耳垂れは出ていない トータルスコア:" + String(currentScore);
    message = text1;
    label2 = "少し出ている";//2
    text2 = "少し出ている トータルスコア:" + String(Number(currentScore)+2);
    message = text2;
    label3 = "たくさん出ている";//4
    text3= "たくさん出ている トータルスコア:" + String(Number(currentScore)+4);
    message = text3;
  } 
  console.log(message);
  let result;
  if (mes == "耳垂れは出ていない トータルスコア"  || mes == "少し出ている トータルスコア" || mes == "たくさん出ている トータルスコア" ) {
    
    if (currentScore <= 5) {
      result="急性中耳炎は軽症です。抗生物質は使わずに3日間様子を見ましょう。"
    } else if (currentScore < 11) {
      result="急性中耳炎は中等症です。 AMPCを高用量で3～5日間投与しましょう。"
    } else {
      result="急性中耳炎は重症です。AMPCを高用量で使うか、CVA / AMPC１：１４製剤を通常量で使うか、CDTR-PIを高用量で使いましょう。鼓膜切開も考慮しましょう。"
    }
    return client.replyMessage(event.replyToken, {
        type: 'text', 
        text:result    
    });
  
  }
  
//再診時プログラム
  if (message == "抗生剤非投与") {
    result = "推奨治療：AMPC常用量3～5日投与";
    return client.replyMessage(event.replyToken, {
      type: 'text', 
      text:result    
  });
  }else if (message=="AMPC常用量"){
    result = "推奨治療：感受性を考慮し以下のいずれかを３～５日間投与。 AMPC高用量 or CVA / AMPC１：１４製剤 or CDTR-PI常用量";
    return client.replyMessage(event.replyToken, {
      type: 'text', 
      text:result    
  });
  }else if (message=="AMPC高用量 または CDTR-PI常用量"){
    result = "推奨治療：感受性を考慮し以下のいずれかを３～５日間投与＋鼓膜切開を考慮。AMPC高用量 or CVA / AMPC１：１４製剤 or CDTR-PI高用量";
    return client.replyMessage(event.replyToken, {
      type: 'text', 
      text:result    
  });
  }else if(message=="CVA / AMPC１：１４製剤 または CDTR-PI高用量"){
    result = "推奨治療：感受性を考慮し以下のいずれかを３～５日間投与＋鼓膜切開考慮。CVA / AMPC１：１４製剤 or CDTR-PI高用量 or TBPM-PI常用量 or TFLX常用量";
    return client.replyMessage(event.replyToken, {
      type: 'text', 
      text:result    
  });
  }else if(message=="TBPM-PI常用量 または TFLX常用量"){
    result = "推奨治療：感受性を考慮し以下のいずれかを３～５日間投与＋鼓膜切開考慮。TBPM-PI高用量 or TFLX常用量。または下記のいずれかを3日間点滴静注。ABPC 150mg/kg/日,分3 or CTRX 60mg/kg/日,分2または分3（新生児は50mg/kg/日以下）";
    return client.replyMessage(event.replyToken, {
      type: 'text', 
      text:result    
  });
  }
  
  
  else {
    return client.replyMessage(event.replyToken, {
      "type": "text", 
      "text": question,
      "quickReply": { 
        "items": [
          {
            "type": "action", 
              "action": {
              "type": "message",
              "label": label1,
              "text": text1
            }
          },
          {
            "type": "action",
              "action": {
              "type": "message",
              "label": label2,
              "text": text2
            }
          },
          {
            "type": "action", 
            "action": {
              "type": "message",
              "label": label3,
              "text": text3
            }
          }
        ]
      }
    });
   } 
}
```

**5.Bot本体をHerokuにデプロイ**
こちらの記事を参考にしました。
[LINEのBot開発 超入門（前編） ゼロから応答ができるまで](https://qiita.com/nkjm/items/38808bbc97d6927837cd)

**6.Webhookを設定**

上記の5.6の作業をこちらの記事を参考に行いました。
[LINEのBot開発 超入門（前編） ゼロから応答ができるまで](https://qiita.com/nkjm/items/38808bbc97d6927837cd#bot%E6%9C%AC%E4%BD%93%E3%82%92heroku%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B)

**7.リッチメニューの設定**
こちらを参考に行いました。
[リッチメニューの画像作成はこうやる！](https://watayoro.com/richmenu)

**8.LINEアプリへのQRコード**
<img width="167" alt="ガイドラインQRコード.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453374/ddc8c570-9c9f-2103-300c-6436d1d8fc54.png">



##考察
LINE Developersのクリックリプライとリッチメニュー使いUIをさらに改善させたLINE Botを作ることができました。
今回も医療者向けのBotになっていますが、一般の方が使えるようなものを開発していきたいと思います。

**追記** (2019.11.21)
一般向けの急性中耳炎重症判定LINE Bot作成しました。
一般の方でも鼓膜を観察できる方法と、豊富なサンプル画像と比較することで自分の鼓膜の状態を判断できる方法を掲載しているWEBサイト（私が管理人）とリンクさせました。
質問に答えていくと急性中耳炎の重症度がわかり、耳鼻咽喉科を受診すべきかどうか分かります。

**動画**
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">一般用 急性中耳炎診断Bot <a href="https://t.co/kd1A77UoK3">pic.twitter.com/kd1A77UoK3</a></p>&mdash; 病気のセルフチェック (@Selfcheckhealt1) <a href="https://twitter.com/Selfcheckhealt1/status/1197337520909869056?ref_src=twsrc%5Etfw">November 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 
**LINEアプリへのQRコード**

<img width="167" alt="一般用QR.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453374/6e9278be-95d8-f22a-2836-99756832d357.png">