誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。

# 最近、膝、痛くない？？
突然ですが、最近、膝痛くないですか？
階段降りる時や立ち上がる時など、特に痛くないですか？？
気づいたら、足がO脚になっていないですか？？？

もしかしたらそれ、変形性膝関節症かもしれません。

＊「変形性膝関節症って何？」という方は、僕が書いたこちらの記事をぜひお読みください。
[変形性膝関節症とは：その治療法・進行予防について](https://clinic.adachikeiyu.com/168)

自分や家族の膝のレントゲン写真を持っている人がどれほどいるのか甚だ疑問ですが、膝の痛みに悩んでいる人の一助になればと作成しました。
（膝のレントゲンは持っていないけど、変形性膝関節症か気になる方は『[膝の写真を送ると変形性膝関節症か判定するbotを作ったので、膝が痛いご両親・ご家族に使ってください。](https://qiita.com/Teru_3/items/bf40b41c18800f9c5126)
』をご利用ください。）

注意）このボットはあくまで参考程度に作成したもので、正確な診断ツールではありません！　
　　　最終的な診断については、おかかりいただいた先生にお伺いください。

# 開発環境の下準備
１）　VScodeのインストール
VScodeのインストールついては、googleなどで他の記事を検索してください。
２）　node.jsとnpmのインストール
Macでの環境作りは、こちらの別の記事にまとめてあります。
参考にしてください。
[Macにnode.js,npmのインストール方法](https://qiita.com/Teru_3/items/cc8a4968eff131d00784)


# 開発環境の準備
１）　kneepainというフォルダを作成
２）　VSCodeで上記フォルダを開き、フォルダ内にkneeOA.jsを作成
３）　VSCode内でターミナルを開き、フォルダをnpm管理できるように初期化

```:terminalコマンド
$ npm init -y
```
4)　フォルダ内にラインボット用のパッケージをnpmでインストール

```:terminalコマンド
$ npm i @line/bot-sdk express
```
#システムの概要
![スクリーンショット 2020-11-22 11.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/9ea404e8-d468-6d23-5b48-75818889e05d.png)

# AIメーカーによる画像認識AIの作成
こちらの記事を参考に、画像認識AIを作成しました。
[画像認識AIを使ったLINE BOTの作り方](https://qiita.com/2zn01/items/7f7b8c57054f67ef9528)

# kneeOA.jsのコード

```javascript:kneeOA.js
'use strict';

const lineAccessToken = '作成したBOTのチャネルアクセストークン';
const lineSecret = '作成したBOTのチャネルシークレット';
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
const config = {
    channelSecret: lineSecret,
    channelAccessToken: lineAccessToken
};
const userID = '作成したBOTのチャネルユーザーID';

// AIメーカーで作成したAIの定数
const aimakerClient = require('request');
const aimakerModelId = AIメーカーで作成したAIモデルのIDを入力;
const aimakerApiKey = 'AIメーカーで作成したAIモデルのAPIキーを入力';
// 返信用定数
const normalrtAdvice = '正常の右膝です。';
const normalltAdvice = '正常の左膝です。';
const kl1rtAdvice = '関節軟骨が痛んでいる可能性のある右膝です。';
const kl1ltAdvice = '関節軟骨が痛んでいる可能性のある左膝です。';
const kl2rtAdvice = '関節裂隙が軽度狭小化した右膝です。';
const kl2ltAdvice = '関節裂隙が軽度狭小化した左膝です。';
const kl3rtAdvice = '関節裂隙が高度狭小化した右膝です。';
const kl3ltAdvice = '関節裂隙が高度狭小化した左膝です。';
const kl4rtAdvice = '関節裂隙が無くなってしまった右膝です。';
const kl4ltAdvice = '関節裂隙が無くなってしまった左膝です。';
const OAImportant = '変形性膝関節症の進行には、膝関節周囲の筋力の衰えることによる、膝関節の不安定性が影響を与えます。';
const OAImportant2 = '膝関節の安定には四頭筋の筋力upが重要です。適切な運動を心がけましょう。';

const app = express();
app.post('/webhook', line.middleware(config), (req, res) =>
{
    Promise
        .all(req.body.events.map(handleEvent))
        .then((result) => res.json(result));
});

const client = new line.Client(config);

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

    const getImageOptions = {
        url: `https://api.line.me/v2/bot/message/${event.message.id}/content`,
        method: 'get',
        headers: {
            'Authorization': 'Bearer ' + lineAccessToken,
        },
        encoding: null
    };

    let resultMessage = '';
    resultMessage = '解析中です。';
    aimakerClient(getImageOptions, function (error, response, body)
    {
        if (!error && response.statusCode == 200)
        {
            console.log('成功');
            //console.log(body);
            const buffer = new Buffer.from(body);
            const base64String = buffer.toString('base64');
            imageRecognition(base64String, event.source.userId);
        } else
        {
            console.log(error);
            resultMessage = restartMessage;
        }
    });

    return client.replyMessage(event.replyToken, [{
        type: 'text',
        text: resultMessage
    }, {
        type: 'text',
        text: OAImportant
    }]);
};

function imageRecognition(base64, userId)
{ // Aiメーカー関数
    let message = '';
    aimakerClient.post({ // AiメーカーAPI接続
        uri: "https://aimaker.io/image/classification/api",
        headers: {
            "Content-type": "application/x-www-form-urlencoded",
        },
        timeout: 20000,
        form: {
            id: aimakerModelId,
            apikey: aimakerApiKey,
            base64: base64
        }
    }, function (error, response, body)
    {
        if (error)
        {
            console.log('imegerecエラー');
            console.log(error);
            message = restartMessage;
        } else
        {
            console.log(body);
            var imageScores = JSON.parse(body);
            var labels = imageScores.labels.sort(function (a, b)
            {
                if (a.score > b.score) return -1;
                if (a.score < b.score) return 1;
                return 0;
            });
            console.log(imageScores.labels);
            if (labels[0].label && labels[0].score)
            {
                switch (labels[0].label)
                {
                    case 'Normal:Rt':
                        message = 'この膝は「' + labels[0].label + '」です。' + normalrtAdvice;
                        break;
                    case 'Normal:Lt':
                        message = 'この膝は「' + labels[0].label + '」です。' + normalltAdvice;
                        break;
                    case 'KL1:Rt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl1rtAdvice;
                        break;
                    case 'KL1:Lt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl1ltAdvice;
                        break;
                    case 'KL2:Rt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl2rtAdvice;
                        break;
                    case 'KL2:Lt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl2ltAdvice;
                        break;
                    case 'KL3:Rt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl3rtAdvice;
                        break;
                    case 'KL3:Lt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl3ltAdvice;
                        break;
                    case 'KL4:Rt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl4rtAdvice;
                        break;
                    case 'KL4:Lt':
                        message = 'この膝は「' + labels[0].label + '」です。' + kl4ltAdvice;
                        break;
                    default:
                        message = 'もう一度、画像を送ってください。';
                        break;
                }
            }
            console.log('アゲイン');
            client.pushMessage(userId, [{
                type: 'text',
                text: message
            }, {
                type: 'text',
                text: OAImportant2
            }]).then();
        };
    });
};

app.listen(PORT);
console.log(`Server running at ${PORT}`);
```
#Herokuへのデプロイ
node.jsが動作する無料のクラウドサーバーであるHerokuにデプロイします。

## 下準備
１）　Heroku CLIのインストール
https://devcenter.heroku.com/articles/heroku-cli

２）　Herokuアカウントの作成
https://signup.heroku.com/

## デプロイ
ターミナルで下記コマンドを実行します。

```:terminalコマンド
$ heroku login
```
実行するとブラウザが自動的に起動するので、ログインします。
すると、ターミナル上でもログインが完了します。

その後、package.jsonの"scripts"の中に、"start"を下記のように追記します。

```:package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node kneeOA.js"
  },
```
Heroku内にデプロイ先のディレクトリを作成します。
下記の順でターミナルにコマンドを入力します。

```:terminalコマンド
$ git init
```

```:terminalコマンド
$ heroku create
```
Heroku内にデプロイ先のディレクトリが作成されると、ターミナルに下記のように表示されます。

```:terminal
Creating app... done, ⬢ デプロイ先の名前
https://デプロイ先の名前.com/ | https://git.heroku.com/デプロイ先の名前.git
```
このデプロイ先ディレクトリに、作成したディレクトリを入れていきます。
下記の順でターミナルにコマンドを入力します。

```:terminalコマンド
$ git add .
```

```:terminalコマンド
$ git commit -m 'init'
```

```:terminalコマンド
$ git push heroku master
```
最終的に下記のように表示されれば完了です。

```:terminal
remote:        https://デプロイ先の名前.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/デプロイ先の名前.git
 * [new branch]      master -> master
```
これでデプロイ完了です。
## ファイルを更新する時
まず、更新するファイルが入ったディレクトリがherokuのデプロイ先のディレクトリと紐づいているかを確認します。

```:terminalコマンド
$ git remote -v
```

### 紐づいている場合
紐づいている場合は、ターミナルに下記のように表記されます。

```:terminal
heroku https://git.heroku.com/heroku上のデプロイ先の名前 (fetch)
heroku https://git.heroku.com/heroku上のデプロイ先の名前 (push)
```

### 紐づいていない場合
紐づいていない場合は、ターミナルに下記のように表記されます。

```:terminal
fatal: not a git repository (or any of the parent directories): .git
```

その場合は、ターミナルに下記の順に入力し、空のディレクトリを作成し、heroku上のデプロイしたいディレクトリと紐付けを行います。

```:terminalコマンド
$ git init　//空のディレクトリを作成
```
```:terminalコマンド
$ heroku git:remote -a heroku上のデプロイしたいディレクトリ名　
　　//herokuのデプロイ先と先ほど作成した空のtディレクトリを紐付け
```

その上で、下記の順にコマンドを入力し、ファイルを更新します。

```:terminalコマンド
$ git add .
```

```:terminalコマンド
$ git commit -m 'init'
```

```:terminalコマンド
$ git push heroku master
```

# LINE Developersからbot作成
１）　LINE Developersにアクセスし、LINEアカウントでログイン
２）　プロバイダー作成
３）　新規チャンネルの作成
４）　チャネルアクセストークンとチャネルシークレットの取得
１）〜４）までは下記を参考に進みました。
[1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)
５）　webhookの設定
Herokuから取得したデプロイ先のURLを入力。
/webhookをつけることを忘れずに。
![スクリーンショット 2020-11-22 12.20.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/964dc27f-002a-37b3-f795-7d8a6007afaf.png)

#完成したラインbot
 完成したラインbotのQRコードがこちらです。
![スクリーンショット 2020-11-22 14.48.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/7a9009d3-b6ef-62a9-8e9a-d86c6c52cefb.png)

ちなみに動作はこんな感じになります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">動作はこんな感じです。 <a href="https://t.co/J1rZ5U0pWo">pic.twitter.com/J1rZ5U0pWo</a></p>&mdash; 北城雅照@足立慶友整形外科 (@kutuyanomusuko) <a href="https://twitter.com/kutuyanomusuko/status/1330494785673687041?ref_src=twsrc%5Etfw">November 22, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
繰り返しになりますが、こちらは診断ツールではありませんので、ご注意ください！！

# その他の記事
１）[近すぎると小池都知事が『密です。』と連呼するデバイスを作ったら腹筋が崩壊したので、皆さんにも試して欲しい。](https://qiita.com/Teru_3/items/4570bd76d8feeaa37db4)
２）[憧れのギニュー特戦隊の誰に似てるか判定するLINEbotを作ったから、ぜってぇ見てくれよなっ！](https://qiita.com/Teru_3/items/109517e6a6e3b761285d)
３）[膝の写真を送ると変形性膝関節症か判定するbotを作ったので、膝が痛いご両親・ご家族に使ってください。](https://qiita.com/Teru_3/items/bf40b41c18800f9c5126)