#患者さんご家族にとって便利なLINE Botを作りたい
福岡で在宅医療を中心とした医療機関を運営しています。
当院の患者さんやご家族、もしくは当院に診察を依頼したいと考えてくださっている方向けに、必要な情報にアクセスできるLINE Botを作ろうと思い取り組んでいる経過です。
#環境・利用API
node v16.10.0
Visual Studio Code 1.60.2
axios 0.22.0
ngrok 2.3.40
#お薬APIは有料のため断念
API連携の方法を学び、在宅医療で役にたちAPIで持って来れる情報に何があるかを考えました。
当初、薬の情報がAPIでもってこれれば、お薬の表面に書いてある文字や、色形で薬が何の薬か判断できるようなり、治療の経過で「血圧の薬だけ明日から抜く」などを患者さんご家族でもやりやすくなるかと考えましたが、薬の情報を使えるAPIは有料のものばかりのようで断念(いいAPIがあれば教えてください)。
#お天気APIとの連携
次に考えたのが、お天気APIとの連携です。
病院で入院や外来を担当していたときはあまり天気を気にしていませんでしたが、在宅医療に携わるようになって晴れか雨かはもちろん、夏の暑さや冬の暑さが自分自身にダイレクトに関わるようになりました。
また、患者さんも引きこもりがちな方が多いため散歩を勧めるけど暑すぎたり寒すぎたりする日は難しかったり、特に高齢者は暑い寒いを感じる体のセンサーが弱っていて暑い中でもクーラーをつけずに過ごしすぐに熱中症になる方がいたりと、お天気情報の重要性を感じています。
そこで、天気の情報をAPIでもってきて、その日の天気に合わせてメッセージを送れるようにならないかと考えた次第です。
#OpneWeatherMapを使う
天気APIは無料のものがいくつかありますが、今回は最も代表的なOpenWeatherMapを使いました。
使用するのにAPIキーを発行する必要がありますが、これについてはこちらのサイトを参照しました。
最終的にはLINE Botに乗せることが目標ですが、何せ初心者なのでまずはOpenWeatherMapから必要な情報を抜き出せるかを試してみました。
#その日の天気や気温に合わせてコメントを出す
http://api.openweathermap.org/data/2.5/weather?q=fukuoka&appid={APIアクセスキー}&lang=ja&units=metric
発行したAPIアクセスキーを該当部に入れ、当院は福岡にあるので場所はfukuokaとして指定、摂氏で温度が欲しいので最後をmetricとしています。
すると、
{
coord: {
lon: 130.4181,
lat: 33.6064
},
weather: [
{
id: 803,
main: "Clouds",
description: "曇りがち",
icon: "04n"
}
],
base: "stations",
main: {
temp: 23.84,
feels_like: 23.87,
temp_min: 23.44,
temp_max: 24.01,
pressure: 1020,
humidity: 61
},
visibility: 10000,
wind: {
speed: 3.6,
deg: 330
},
clouds: {
all: 75
},
dt: 1634121790,
sys: {
type: 1,
id: 7998,
country: "JP",
sunrise: 1634073664,
sunset: 1634114871
},
timezone: 32400,
id: 1863967,
name: "福岡市",
cod: 200
}
と、JSONで情報が返ってきます。
この情報を使って、晴れの日には「いいお天気ですよ」、曇りの日には「お散歩はいかがですか」、雨の日には「お出かけの際は傘をお持ちください」と表示し、さらに最高気温が30度以上の場合は「熱中症にご注意ください」、32度以上の場合は「クーラーを入れてください」と表示することに。
コードこちら
// axiosライブラリを呼び出す
const axios = require('axios');
// 実際にデータを取得する getRequest 関数
async function getRequest() {
let response;
try {
response = await axios.get('http://api.openweathermap.org/data/2.5/weather?q=fukuoka&appid=42d222cdac5c766d1af96314e63f948d&lang=ja&units=metric');
if (response.data.weather[0].main === 'Clouds'){
console.log('お散歩はいかがですか');
}
if (response.data.weather[0].main === 'Rain'){
console.log('お出かけの際は傘をお持ちください');
}
if (response.data.weather[0].main === 'Clear'){
console.log('いいお天気ですよ');
}
if (response.data.main.temp_max > 30){
console.log('熱中症にご注意ください');
}
if (response.data.main.temp_max > 32){
console.log('クーラーを入れてください');
}
else{
console.log('以上です');
}
} catch (error) {
console.error(error);
}
}
// getRequest を呼び出してデータを読み込む
getRequest();
これで、その日の福岡の天気や気温に合わせた情報が返ってくるようになりました。
#LINE Botと連携する
次に、これをLINE Botと連携します。
せっかくなので、上記で試したif文をつかって天気情報によってLINEで返信するものを作りたかったのですが、何度やってもエラーが出てうまくいかず、、
今の力の限界ということで、シンプルに天気を返してくれるものを作りました。
'use strict';
// ########################################
// 初期設定など
// ########################################
// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
// ローカル(自分のPC)でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;
// Messaging APIで利用するクレデンシャル(秘匿情報)です。
const config = {
channelSecret: '作成したBotのチャネルシークレット',
channelAccessToken: '作成したBotのチャネルアクセストークン'
};
// ########## ▼▼▼ サンプル関数 ▼▼▼ ##########
const sampleFunction = async (event) => {
const userText = event.message.text;
let replyText = '';
// 部分一致1(「範囲」という文字が1ヶ所でも含まれていたら反応)
if (userText.indexOf('範囲') > -1) {
replyText = 'https://taro-cl.com/for_patients#b';
}
// 部分一致2(「費用」という単語が1ヶ所でも含まれていたら反応)
if (userText.indexOf('費用') > -1) {
replyText = 'https://taro-cl.com/q_and_a';
}
// 部分一致3(「天気」という単語が1ヶ所でも含まれていたら反応)
if (userText.indexOf('天気') > -1) {
let response = await axios.get('http://api.openweathermap.org/data/2.5/weather?q=fukuoka&appid=42d222cdac5c766d1af96314e63f948d&lang=ja&units=metric');
console.log(response.data.weather[0].main);
replyText = '「今日の天気は' + response.data.weather[0].description + 'です」';
}
// この時点でどの条件にも引っかかってない(replyTextが空文字列のまま)なら相槌をうっておく
if (replyText === '') {
replyText = '当院にお問い合わせください(092-410-3333)';
}
return client.replyMessage(event.replyToken, {
type: 'text',
text: replyText
});
};
// ########## ▲▲▲ サンプル関数 ▲▲▲ ##########
// ########################################
// LINEサーバーからのWebhookデータを処理する部分
// ########################################
// LINE SDKを初期化します
const client = new line.Client(config);
// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
// 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
// サンプル関数を実行します
return sampleFunction(event);
}
// ########################################
// Expressによるサーバー部分
// ########################################
// expressを初期化します
const app = express();
// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {
// 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
if (req.body.events.length === 0) {
res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します(なくてもよい)
console.log('検証イベントを受信しました!'); // ターミナルに表示します
return; // これより下は実行されません
} else {
// 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
console.log('受信しました:', req.body.events);
}
// あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
// 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});
// 最初に決めたポート番号でサーバーをPC内だけに公開します
// (環境によってはローカルネットワーク内にも公開されます)
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
天気情報に加えて、尋ねられることが多い「費用」と「訪問範囲」について、それぞれ「費用」「範囲」という言葉が含まれる送信があれば、それぞれを説明しているホームページ部分のリンクを返信するようにしました。
実際に作動している様子がこちらです。
https://youtu.be/FobBOvWumrU
なんとか動いてくれました。
自分の学びに合わせてLINE Botもアップデートしていきたいと思います。