Edited at
PORTDay 16

夫の帰宅時間をお知らせしてくれるLINE bot

この記事は、PORT Advent Calendar 2018の16日目です。

Vue.jsかfirebaseかAMPについて書こうと思いつつ、、夫の帰宅時間連絡の手間を思いついたので、解消するものを考えました。


解決したい課題

旦那氏の帰宅時間がわかるようにしたい。私も旦那氏もお腹ペコペコ状態なので、ご飯をすぐ食べられる状態にしたいから。

旦那氏は日によって出向したりするので、毎日同じオフィスから帰宅するわけではありません。

なので、毎日帰宅時間をたずねていました。聞くのも手間だし、調べる方も手間。


解決方法

旦那氏「名古屋から乗ったよ」みたいな文章をLINEで送信したら、botが勝手に乗り換え検索してくれて、最寄駅の到着時間を返信してくれたらいいんじゃない?

よくないかもしれないけど、試しに作ってみようのモチベーション。初めてのLINE bot作成です。


参考

LINEのBot開発 超入門(前編) ゼロから応答ができるまで

こちらの記事を参考にさせていただきました。

環境Node.jsとHerokuだったので、それに従います。初めてのHeroku。

こちらがHeroku公式。デプロイまではこれも参考に。Getting Started on Heroku with Node.js


とりあえず参考の通りにした

めっちゃ飛ばしましたけど、、

LINEのBot開発 超入門(前編) ゼロから応答ができるまでさんが超優秀なので、ほとんどその通りにしました。ちょこちょこ詰まりはしたけれど、そこは公式を参考にしつつ、「こんにちは」って送信したら、「これはこれは」って返ってくるようになりました。

IMG_0569.PNG

アイコンはホシガメちゃんにしました。(向き反対の方が喋ってる感でたかもしれない)

「メッセージありがとうございます」みたいな返信はデフォルトメッセージで、LINE Developersの設定から削除することができました。


index.js

require('dotenv').config()

const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000
const line = require('@line/bot-sdk')
const config = {
channelAccessToken: process.env.LINE_ACCESS_TOKEN,
channelSecret: process.env.LINE_CHANNEL_SECRET
}
const app = express()
const bot = new line.Client(config)

app.post('/webhook', line.middleware(config), (req, res, next) => {
res.sendStatus(200)
let events_processed = []
req.body.events.forEach((event) => {
if (event.type == "message" && event.message.type == "text"){
if (event.message.text == "こんにちは"){
events_processed.push(bot.replyMessage(event.replyToken, {
type: "text",
text: "これはこれは"
}))
}
}
})

app.listen(PORT, () => console.log(`Listening on ${ PORT }`))


参考までにコードは確かだいたいこんな感じ。


Dialogflowにキーワードを登録

ここも詳しくは超入門見ていただければでてきますが、

Dialogflowってサービスにキーワードを登録すると、Node.jsからの判別が簡単になります。

表記揺れ登録で、言語処理が簡単になるって感じ。

私は以下の言葉について登録しました。


Entities登録


帰るって意味を汲み取る(kitaku)

スクリーンショット 2018-12-10 23.59.12.png


乗る駅(ekimei)

名古屋駅=>名古屋名駅なごや


Intentsに例文を登録

スクリーンショット 2018-12-11 0.03.12.png

例文登録すると、勝手にEntitiesと紐づけてくれます。


イベント登録

登録した単語が来たらフックになるようにAction and parametersイベント登録します。

スクリーンショット 2018-12-11 0.10.50.png

Node.jsに接続するのにGoogle Cloud Platformを介したりして、ちょっと面倒ですが、使えるようになります。


index.js

// 省略

app.post('/webhook', line.middleware(config), (req, res, next) => {
res.sendStatus(200)
let events_processed = []
req.body.events.forEach((event) => {
if (event.type == "message" && event.message.type == "text"){
if (event.message.text == "こんにちは"){
events_processed.push(bot.replyMessage(event.replyToken, {
type: "text",
text: "これはこれは"
}))
}

events_processed.push(
session_client.detectIntent({
session: session_client.sessionPath(process.env.GOOGLE_PROJECT_ID, event.source.userId),
queryInput: {
text: {
text: event.message.text,
languageCode: "ja",
}
}
}).then((responses) => {
if (responses[0].queryResult && responses[0].queryResult.action == "handle-kitaku"){
let message_text
let from = responses[0].queryResult.parameters.fields.ekimei.stringValue
let to = process.env.EKIMEI_TO
if (from){
message_text = `ご帰宅ですね!${from}からですね!`
return bot.replyMessage(event.replyToken, {
type: "text",
text: message_text
});
} else {
message_text = `ご帰宅ですね!どこから?`;
return bot.replyMessage(event.replyToken, {
type: "text",
text: message_text
})
}
}
})
)
}
})
})


名古屋駅から帰宅するのを察してくれるようになりました。

ここまでは、参考そのままな感じ。


乗り換え検索ができるようにならなければならない

旦那氏の帰宅時間をお知らせしてもらいたいので、経路検索のAPIを叩かなければいけません。

今回は駅すぱあとさんのフリープランを使用しました。

申し込みが必要でしたが、レスポンスが早く、サクッとアクセスキーが使えるようになりました。

検索結果のURLを返してくれるようになります。

スクリーンショット 2018-12-11 20.43.50.png

こちらを導入するにあたり、存在する駅名にDialogflowのキーワードを変更しておきました。

Entities 名古屋駅=>名古屋名駅なごや

にしていましたが、名古屋駅だと検索にひっからなかったので、名古屋に変更。


なんだかんだAPI叩けた


index.js

// events_processed.pushにapiを追加する

events_processed.push(
session_client.detectIntent({
session: session_client.sessionPath(process.env.GOOGLE_PROJECT_ID, event.source.userId),
queryInput: {
text: {
text: event.message.text,
languageCode: "ja",
}
}
}).then((responses) => {
if (responses[0].queryResult && responses[0].queryResult.action == "handle-kitaku"){
let message_text
let date = moment().format('YYYYMMDD')
let time = moment().format('HHmm')
let from = responses[0].queryResult.parameters.fields.ekimei.stringValue
let to = process.env.EKIMEI_TO
if (from){
const url = `https://api.ekispert.jp/v1/json/search/course/light?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&date=${date}&time=${time}&searchType=departure&plane=false&shinkansen=false&limitedExpress=true&redirect=false&contentsMode=sp&key=${process.env.EKISPERT_KEY}`
axios.get(url).then((res) => {
message_text = `ご帰宅ですね!${from}からですね!\n${res.data.ResultSet.ResourceURI}`
return bot.replyMessage(event.replyToken, {
type: "text",
text: message_text
})
}).catch((err) => {
message_text = `エラー!${err}`
return bot.replyMessage(event.replyToken, {
type: "text",
text: message_text
});
})
} else {
// 略
}
}
})
)
}
})

IMG_0570.PNG

やったー!と思ったのもつかの間。

時間が日本じゃないことは明白。

herokuの時間を日本に変更する必要がありました。

heroku config:add TZ=Asia/Tokyo --app [app_name]

参考にした

これで時間が合いました。


感想

帰宅連絡だけで埋め尽くされていたLINEが、botを含めたグループを作成したことで、帰宅用にスレッドが分かれたのが地味に良かったです。

これだと直近の電車しか探せないので、何分前に電車に乗ったのか、何分後に駅につくのかみたいな感じで、検索できるようにしたらいいかなと。

参考を見ると、botの対応ももっとチューニングできるらしいので、そこもやってもいいかもしれません。



旦那氏はそこそこ喜んでくれました。


最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なRubyエンジニアを募集しています(Rubyエンジニア以外も)。

もくもく会も行なっていますので、ぜひ一緒にもくもくしましょう!

PORTもくもく会

(私は残念ながらリモート勢なのでいませんが・・)

PORT Advent Calendarはまだまだ続きます。