この記事は、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開発 超入門(前編) ゼロから応答ができるまでさんが超優秀なので、ほとんどその通りにしました。ちょこちょこ詰まりはしたけれど、そこは公式を参考にしつつ、「こんにちは」って送信したら、「これはこれは」って返ってくるようになりました。
アイコンはホシガメちゃんにしました。(向き反対の方が喋ってる感でたかもしれない)
「メッセージありがとうございます」みたいな返信はデフォルトメッセージで、LINE Developersの設定から削除することができました。
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)
乗る駅(ekimei)
名古屋駅
=>名古屋
、名駅
、なごや
Intentsに例文を登録
例文登録すると、勝手にEntitiesと紐づけてくれます。イベント登録
登録した単語が来たらフックになるようにAction and parameters
イベント登録します。
Node.jsに接続するのにGoogle Cloud Platformを介したりして、ちょっと面倒ですが、使えるようになります。
// 省略
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を返してくれるようになります。
こちらを導入するにあたり、存在する駅名にDialogflowのキーワードを変更しておきました。
Entities 名古屋駅
=>名古屋
、名駅
、なごや
にしていましたが、名古屋駅
だと検索にひっからなかったので、名古屋
に変更。
なんだかんだAPI叩けた
// 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 {
// 略
}
}
})
)
}
})
時間が日本じゃないことは明白。
herokuの時間を日本に変更する必要がありました。
heroku config:add TZ=Asia/Tokyo --app [app_name]
参考にした
これで時間が合いました。
感想
帰宅連絡だけで埋め尽くされていたLINEが、botを含めたグループを作成したことで、帰宅用にスレッドが分かれたのが地味に良かったです。
これだと直近の電車しか探せないので、何分前に電車に乗ったのか、何分後に駅につくのかみたいな感じで、検索できるようにしたらいいかなと。
参考を見ると、botの対応ももっとチューニングできるらしいので、そこもやってもいいかもしれません。
最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なRubyエンジニアを募集しています(Rubyエンジニア以外も)。
もくもく会も行なっていますので、ぜひ一緒にもくもくしましょう!
PORTもくもく会
(私は残念ながらリモート勢なのでいませんが・・)
PORT Advent Calendarはまだまだ続きます。