enebular + LINEbot + Google Cloudを利用した蘇生処置補助ボット　〜プログラミング初心者の放射線診断科医が作ってみた〜

## この記事の内容
・はじめに
・蘇生処置補助ボットが使える環境
・蘇生処置補助ボットでできること
・蘇生処置補助ボットの仕組み
・実際の挙動
・技術の詳細
・技術的課題
・改善したい部分
・おわりに
・参考記事

##
:::note alert
注意
技術的な課題が複数あり、現段階ではまだ使用を推奨しません。
:::

## はじめに
はじめまして。画像診断を専門にする放射線科医です。
健康診断などで撮影される「レントゲン写真」も、画像検査の一つです。
画像検査には「CT」、「MRI」といったより精密な検査があります。その撮影された画像から病気を探し、評価して依頼医の先生に伝える仕事をしています。

CTやMRIでは「造影剤」というお薬を使うことがあり、病気の種類によってはこれを使うことで体の情報をより詳細に評価できるようになります。
しかし、ごく稀に重いアレルギー反応が起こって、重篤な状態に急変することがあります。

急変時には短時間で的確な処置を行なっていく必要がありますが、救急外来のような日常的に急変患者さんの対応をするわけではない人にとっては、たとえ医療従事者でもストレスを感じずに冷静に対処するのは難しいものがあります。

検査に立ち会う関係上、この急変時に補助をしてくれるものがあったらいいな、と思っていました。


今回プログラミングに触れる機会があり、初心者ながら作成してみました。

## 蘇生処置補助ボットが使える環境
以下の環境での使用を前提に作成しています。
・蘇生処置を習得した人がいる環境（=病院内での使用）
・オンライン環境

## 蘇生処置補助ボットでできること
・LINEトーク画面でのボタン操作で、その状況における蘇生処置の次の一手候補を表示させる。
・応援要請の電話番号、応援を呼びたい場所などをメッセージに表示させる。
・胸骨圧迫における自己心拍確認や、ノルアドレナリン投与から次の投与を検討するまでの時間を計測し、確認タイミングでメッセージを表示させる。
・処置内容とそれを行なった日時を秒単位までスプレッドシートに自動記載

#### 操作画面

![スクリーンショット 2024-08-10 10.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/e3360ba4-7302-5467-7387-9f4c48ff6b4d.png)
図左→右に進行。矢印(黄色)のリッチメニューやクイックリプライを押すとフローが進行する。

#### スプレッドシート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/36b65bf9-af21-9dfa-0c72-c68f89450626.png)
蘇生処置を行なった場所と開始日時を自動記載。
LINEbot上で入力した内容を、入力した時刻とともに自動記載。
時刻の形式(:→時、分、秒、年月日不要など)はenebular上で柔軟に変更可能。

## 蘇生処置補助ボットの仕組み
・enebular内で蘇生処置アルゴリズムをフロー化
・LINEbot と enebularを連携、LINEbot のメッセージをフロー内で条件分岐させ次の処置内容をリプライさせる
・リプライメッセージにクイックリプライを組み込み、これとリッチメニューで直接文字入力を省略、フロー外への逸脱防止
・リプライメッセージ内には予め投薬量や応援を呼ぶための電話番号、応援を呼びたい場所情報を格納させ、参照すべき情報源を削減
・処置内容とその処置を行なった時刻をgoogle cloudでgoogle sheetへ出力、sheetの共有でその後の引き継ぎを円滑化

## 実際の挙動

こんな感じで動きます。
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">もいせん退院制作で作った蘇生処置補助ボットの、実際の挙動の画面収録動画。造影剤アナフィラキシーショック発生時の対応補助を念頭に作成。<br>タイマーの時間はデモ用にちょっと短く設定しています。qiitaで記事を公開予定。<a href="https://twitter.com/hashtag/%E3%82%82%E3%81%84%E3%81%9B%E3%82%93?src=hash&amp;ref_src=twsrc%5Etfw">#もいせん</a><a href="https://twitter.com/hashtag/NODERed?src=hash&amp;ref_src=twsrc%5Etfw">#NODERed</a><a href="https://twitter.com/hashtag/LINEbot?src=hash&amp;ref_src=twsrc%5Etfw">#LINEbot</a><a href="https://twitter.com/hashtag/GoogleCloud?src=hash&amp;ref_src=twsrc%5Etfw">#GoogleCloud</a> <a href="https://t.co/aXek8MAG8O">pic.twitter.com/aXek8MAG8O</a></p>&mdash; nekonomakura (@dVvdZmj3rU9524) <a href="https://twitter.com/dVvdZmj3rU9524/status/1822110500240650256?ref_src=twsrc%5Etfw">August 10, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

以下は、実際の使用シーンがわかりやすいように、両サイドにスタッフのやりとりを吹き出しで表示した画像です。上から下に順番に進みます。

![スクリーンショット 2024-08-10 11.04.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/9a10bc5f-09af-f3c8-8622-3ed42c6dbd60.png)

リッチメニュー左上の「Start!」を押します。

![スクリーンショット 2024-08-10 11.05.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/ffe884a1-d48a-a31c-0f90-14a0704d1b35.png)
患者さんの状態確認を確認し、クイックリプライで合致するものを選択していきます。

![スクリーンショット 2024-08-10 11.06.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/ed256b07-c1db-ed92-4d49-8c44473e9677.png)
処置の内容確認と、状況整理のメッセージ。これに返信することで、次の応援要請の住所情報が分岐します。

![スクリーンショット 2024-08-10 11.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/a473dba1-cac5-2c23-e148-84c5ff8b76d9.png)
提示に従って処置の指示を出していきます。

![スクリーンショット 2024-08-10 11.07.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/da1b5d16-7a90-f267-209b-9afef909b95d.png)

![スクリーンショット 2024-08-10 11.07.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/5296e370-165e-f42e-ce1a-1c639779f478.png)
リッチメニューの「胸骨圧迫開始！」「ノルアド投与！」を押すと、それぞれ2分、4分のカウントが開始されます。その時間が経過したらメッセージで通知が来ます。

![スクリーンショット 2024-08-10 11.08.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/28e2ba74-6891-300f-d14e-091159bcbb02.png)
リッチメニューの「処置候補」を選択し、次の手を視覚的に把握

![スクリーンショット 2024-08-10 11.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/2e0c9a43-6efd-04b2-84c8-7bd3111cd82d.png)


![スクリーンショット 2024-08-10 11.08.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/67eecfe9-68d1-3a92-1ab9-aafe1ed0a18b.png)
胸骨圧迫開始から2分経過した通知

![スクリーンショット 2024-08-10 11.09.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/99f1b270-8cd2-d73a-9fd0-649366710097.png)
自己心拍再開を確認、クイックリプライのROSCまたはリッチメニューの「Finish.」でフローが終了します。

## 技術の詳細

### LINEbot と enebularの連携
###
大枠はこちらの記事を参考にさせて頂きました。
フローも公開されており、ダウンロードして使用できます。

https://qiita.com/tatsuya1970/items/b3521f090df400b71e2b

webhook部分は当初、　node-red-contrib-line-messaging-api　に含まれるものを使用していましたが、クラウド実行環境を用いるために上記記事を参考に作り直しました。

### LINEbotによるメッセージ提示
###
LINEbot のreply の機能を使っています。

https://developers.line.biz/ja/docs/messaging-api/sending-messages/#send-messages-at-any-time

https://zenn.dev/protoout/articles/11-nodered-linebot-cheatsheet

LINEbotに対しユーザーがメッセージを送ると、そのメッセージには「応答トークン」と呼ばれる、返信する為に必要な情報がenebular側に渡されます。この応答トークンを用いることで、メッセージを送信したユーザーに対し返信することができます。

応答トークンには以下の制約があります。
・一つの応答トークンで、最大5つまでのメッセージが返信できる。
・返信されるメッセージは、同時に送信されなければならない。
・一度使用した応答トークンは、その後使用できない。
・応答トークンは、発行されてから使用されるまでの時間に限りがある。

なので、たとえばタイマーを起動するときに
ユーザーが「タイマー起動」とメッセージをLINEbotへ送信
（例として、応答トークンA111を発行）

→「タイマーを起動！２分間のカウントを開始します」とLINEbot側が返信
（ここで応答トークン（A111)が使用される。）

→「２分間経ちました！自己心拍再開を確認してください！」とLINEbot→ユーザーでメッセージを送りたいが、応答トークンが既に使用済みで使えないため、ユーザー側に送信できない状況となります。

なので、「タイマーを起動！２分間のカウントを開始します」をユーザー側に送信させるなどして、トーク履歴上で状況把握しやすくかつ応答トークンが使用できるように工夫しました。

PUSH通知機能を使えば解決しますが、無料だと送れるメッセージ数に限りがあるため今回は実装しませんでした。

### enebular内でのフロー
###

![スクリーンショット 2024-08-10 11.58.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817490/cbdf52ae-1715-d043-cedc-3775f2906fee.png)
フローの概観です。わかりやすくするため、nodeを一部削っています。

https://qiita.com/tatsuya1970/items/b3521f090df400b71e2b

重ねての紹介ですが、こちらのフローをベースに作成しています。記事と重複しそうな内容は省略します。

LINEbotにユーザーが送信した情報は、enebular内で以下のように流れます。

1.ユーザーから送信されたメッセージ内容をswitch nodeで分岐させる。
2.メッセージやクイックリプライを含んだfunction nodeへと受け渡す。switch nodeの一部の出力からは2つのnodeへと出力し、一方はgoogle sheetへの記載、他方はLINEbotへのリプライのフローに分流させる。

3a-1.function nodeからLINEbotへと返信させ、ユーザーがメッセージを閲覧できるようになる。
3b-1.function nodeから日時を取得するfunction nodeへと接続し、[日時、処置内容]と配列形成。
3b-2.google sheet へと[日時、処置内容]の配列を渡し、下に追記する設定で記載していく。


このフローを絶えず正確に動かすには、入力される単語や文を限定する必要があります。switch nodeでの分岐が大事であり、条件に合致しないテキストが送信された場合に状況と合わないメッセージを送信してしまったり、返信できないといったエラーを生じさせる可能性があるからです。

このため、LINEbot側でリッチメニューを、enebular側でクイックリプライを実装しました。いずれもボタン操作で、登録されたテキストをLINEbot側に送信できます。この登録されたテキストを、enebularのswitch nodeで分岐させるための条件として設定しておけばボタンを押すだけでフローを動かすことが可能となります。

リッチメニューとクイックリプライは、ユーザーがテキスト入力する時間と労力を省き、ストレスを軽減する意味もあります。

以下、クイックリプライのfunction nodeの記述例です。
```
// 送信したい文字列をresultに退避させておく
const result = msg.payload;

// LINEサーバーからの内容をpayloadに復元する
msg.payload = msg.payloadLINE;

// 返信メッセージをhttp requestの結果にする
const replyMessage = result.text;


var post_request = {
    "headers": {
        "content-type": "application/json; charset=UTF-8",
        "Authorization": " Bearer " + msg.linetoken
    },
    "payload": {
        "replyToken": msg.line.events[0].replyToken,
        "messages": [
            {
                type: 'text',
                text: '患者さんの状態は？',
                quickReply: {
                    items: [
                        {
                            type: 'action',
                            action: {
                                type: 'message',
                                label: '脈を触れない',
                                text: '脈を触れない'
                            }
                        },
                        {
                            type: 'action',
                            action: {
                                type: 'message',
                                label: '血圧が測れない',
                                text: '血圧が測れない'
                            }
                        }
                    ]
                }
            }
        ]
    }
};

return post_request;
```


### その他の工夫

投与量、部位
電話番号
電話で伝えるべき内容
蘇生処置が行われている場所の情報

などを操作の中でスマホ画面だけで把握できるように、予め情報をメッセージ内に格納しました。

## 技術的課題
### 確認した部分
#### 1.enebularのクラウド実行環境でのラグ
enebularで実行する際、フロー編集画面で提示される一時的な連携用のアドレスを用いた場合と比較すると、クラウド環境ではリプライ表示までに若干の遅延が見られました。急いでいる状況でこのラグは好ましくないと私は感じます。

#### 2.院内内線番号などの情報の扱いや第3者の使用
LINEbotを友達登録すると使用できます。そのため、院内の電話番号などを情報として格納していた場合、第3者がフローを動かすとその情報が流れる可能性があります。また、第3者の使用で実際にはない蘇生処置の記録が生じる可能性もあります。第3者の友達登録の完全な制限ができないため、UIとしてLINEbotをそもそも使用しないか、第3者に流れるのが好ましくない情報を格納しない設計が必要だと思います。

https://help.linebiz.com/lineadshelp/s/article/L000002064?language=ja


### 仕様として公表されている部分
#### 3.linebot の応答トークンの挙動保証時間
胸骨圧迫は2分、ノルアドレナリンは4分のdelayを設けていますが、この時間は応答トークンの動作保証の範囲外にあります。テストでは観測されませんでしたが、時間経過メッセージがユーザー側に送信されない可能性があります。プッシュ通知を使用すれば解決しますが、サービス使用料としてのコスト発生の可能性があります。

### 未確認の部分
#### 4.一つの蘇生処置中に別の蘇生処置がはじまった時の挙動
処置終了に合わせてタイマーのメッセージを止めるためにglobalcontextを使用しています。
実際に確認ができていないですが、同時に使用した場合、片方の処置終了によるglobalcontextのoffがもう一方に影響する可能性があると考えています。

## 改善したい部分
上記に加え、以下の点を改善したいです。

#### 1.一つの蘇生処置に対応したgoogle sheetのsheet作成とそれへの記載
今回は蘇生処置用のシートを予め作成し、それに記載していく形にしました。
連続で使用した場合、同じsheetの前回処置の下に追記していくことになります。
たとえばAという患者さんの処置をX日に、Bという患者さんの処置をY日に行なった場合、それぞれ別のシートに記載のあった方が情報を扱いやすいはずです。

実装したいのは以下の流れです。
元となるシート（枠組みを予め記載してある）を読み込む
→新しいシートとして追加保存
→追加保存したシートに処置内容の経時的記載

これができれば、sheetのメンテナンス無しで継続使用できて便利になります。

#### 2.delay の制御
2分のタイマーと4分のタイマーの開始タイミングによっては、二つの通知メッセージが短時間に連続して送られる可能性があります。それぞれのメッセージにクイックリプライを採用しており、先に到着したメッセージでクイックリプライのボタンを押す前に次のメッセージが来ると、最初のクイックリプライがキャンセルされてしまいます。
そのため、クイックリプライメッセージが一度表示された場合、それに対する返信があるまでは次のメッセージを遅延させる制御用のnodeが必要だと考えています。計時の正確性は少し損なわれますが、タスクが溜まることによる使用者のストレス軽減の利益がより勝ると考えます。

#### 3.オフラインで使用できるようにしたい
病院内では電波の悪いところがあり、たとえ院内にオンライン環境が構築されていても使用できない可能性があります。いつでも実行できるようにデバイス内で、オフラインでも稼働するようなネイティブアプリ化を行いたいです。

### feedbackをいただいたもの
#### 4.情報の提示方法の工夫
今回、LINEbot側からはテキストデータで情報を提示しましたが、内容によってはイラストや写真などの画像データの方が、ユーザーが情報を処理しやすい可能性があります。

#### 5.音声読み上げ
今回、LINEbotに表示された文字をユーザーが読み上げる仕様で作成しました。この読み上げまでアプリでやれてしまえば、ユーザーのタスクを一つ減らすことができ、よりストレスが軽減した状態で処置に取り組める可能性があります。

## おわりに
プログラミング初心者の放射線診断科医が蘇生処置補助ボットを作成しました。自分の欲しいものを試行錯誤しながら作る体験ができ、大変でしたが非常に充実した楽しい時間でした。課題は多いですが、少しずつ改良を加えていきたいです。

最後までお付き合いいただき、誠にありがとうございます。

## 参考記事

https://qiita.com/ytktkmr/items/df824bf94e41d0f17336

https://qiita.com/tatsuya1970/items/b3521f090df400b71e2b

https://zenn.dev/protoout/articles/11-nodered-linebot-cheatsheet

https://developers.line.biz/ja/docs/messaging-api/sending-messages/#send-messages-at-any-time

https://qiita.com/yamachan360/items/93d530e18d2ccd62caef

https://qiita.com/tatsuya1970/items/1fd593306334bdfbf509