素人が2020年までの1ヶ月でLINE BOTに挑戦する毎日note. 【Day 26:開発2日目_挨拶を返す】
こんにちは。くろです。
12月1日から2019年残り1ヶ月でスケジュール調整BOTの開発に挑戦しています。今日は26日目です。
昨日からついにアプリケーションの開発がスタートしました。
やはり仕事をしながら、土日も予定入れながらだとなかなか進まないですね。
設計と簡易学習だけで3週間くらいかかってしまいました。
ただ、会社員には有給休暇という素晴らしい制度があるので、
ちょっとチート感ありますが、木金もお休み頂いて、BOT開発を追い込んでいきたいと思います。
開発アプリ:Googleカレンダーと連動してスケジュールを調整してくれるLINEBOT
BOTの概要:Day 2の記事に書いています。
BOTの仕様:機能一覧やフローチャート Day 10の記事に書いています。
BOTの開発環境:Day25の記事でアプリケーションの骨格を作りました。
今日からは骨格に肉付けをしていきます。
まずは、日程調整依頼をする所から。
日程調整依頼のフローチャートはこれです。
1,BOTをグループに招待した時に日程調整マイページへのリンクと共に挨拶メッセージを送る所を作ります。
1-1,友だち追加時とグループ/トークに招待された時に挨拶メッセージを送る。
※オウム返し機能は残しつつ。
switch構文をlinebot関数の中に作って、送られてきたwebhookのevents.typeによって処理を変えるようにしました。
joinだとgreeting_join
followだとgreeting_follow
をそれぞれ実行する形です。ちょっと躓いたポイントがswitchのcase XXをcase "XX"って書かないと動かなかったことです。
1-2,返す挨拶文にリンクをつける。
こんな感じで返すmessageを配列にすると複数のメッセージを返すことができました。5つまで送れるみたいです。
return client.replyMessage(ev.replyToken, [
{
type: "text",
text: `${pro.displayName}さん、こんにちは。調整くんです。\\n${pro.displayName}さんの代わりに、ぼくがスケジュール調整をするよ。`
},
{
type: "text",
text: `このリンクから依頼してね。`
},
{
type: "text",
text: "https://scheduler-linebot.herokuapp.com/"
}
])
実行結果がこちらです。
ちゃんとグループに招待した時に3つのメッセージを送ってくれてます。
また、オウム返しも生きてます。
グループトークの場合は、誰かが発言しないとトークが作られないので、あと発言してみると、しっかり挨拶してくれました。オウム返しも生きているので、オウム返しも一緒にされちゃいますが、後でそのへんは修正します。
1-1,2で作成したコードをおいておきます。
ちょこちょこconsole.logを出していますが、なくても大丈夫です。
index.js
const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000
const line = require("@line/bot-sdk")
const app = express()
const ejs = require('ejs')
const http = require('http');
const fs = require('fs');
const url = require('url');
const qs = require('querystring');
const config = {
channelAccessToken: process.env.ACCESS_TOKEN,
channelSecret: process.env.SECRET_KEY
};
const client = new line.Client(config);
const create = require('./routes/create')
app
.use(express.static(path.join(__dirname, 'public')))
.set('views', path.join(__dirname, 'views'))
.set('view engine', 'ejs')
.get('/', (req, res) => res.render('pages/index'))
.use('/create', create)
.post("/hook/", line.middleware(config), (req, res) => lineBot(req, res))
.listen(PORT, () => console.log(`Listening on ${ PORT }`))
function lineBot(req, res) {
res.status(200).end(); //200番をレスポンスとして返しておく
const events = req.body.events;
console.log(`linebot内のevents`)
console.log(events); // console.log(`eventsは${events}、と${req.body.events}`); \\この書き方だと中身の配列が見えなかった。
const promises = [];
for (let i = 0, l = events.length; i < l; i++) {
const ev = events[i];
console.log(`${i}番目のイベントの中身は`);
console.log(events[i]);
console.log(`ev.typeは`);
console.log(ev.type);
switch (ev.type) {
case "join":
promises.push( //promisesにechoman(ev)の処理を配列として入れるメソッドぽい。
greeting_join(ev) //return の内容が、Promise のthenのコールバック関数に渡る.ここではpromise.push()
);
case "follow":
promises.push(
greeting_follow(ev)
);
case "message":
promises.push(
echoman(ev)
);
}
}
Promise.all(promises).then(console.log("pass"));
}
async function echoman(ev) {
const pro = await client.getProfile(ev.source.userId); //awaitがあるので、この処理を待ってから次の行にいく。
return client.replyMessage(ev.replyToken, {
type: "text",
text: `${pro.displayName}さん、今「${ev.message.text}」って言いました?`
})
}
async function greeting_follow(ev) {
const pro = await client.getProfile(ev.source.userId);
return client.replyMessage(ev.replyToken, [
{
type: "text",
text: `${pro.displayName}さん、こんにちは。調整くんです。\\n${pro.displayName}さんの代わりに、ぼくがスケジュール調整をするよ。`
},
{
type: "text",
text: `このリンクから依頼してね。`
},
{
type: "text",
text: "https://scheduler-linebot.herokuapp.com/"
}
])
}
async function greeting_join(ev) {
return client.replyMessage(ev.replyToken, [
{
type: "text",
text: `みなさん、こんにちは。調整くんです。みんなの代わりに、ぼくがスケジュール調整をするよ。`
},
{
type: "text",
text: `このリンクから依頼してね。`
},
{
type: "text",
text: "https://scheduler-linebot.herokuapp.com/"
}
])
}
分量も多くなったので、今日の記入文はここまでにします。
明日もよろしくお願いします!
下記はリファレンスとして調べたことをまとめています。用語など途中でわからないものがあれば一度参照下さい。
・promise.allって何?
Promise.all() - 複数のPromise関数を実行し、全ての結果を得る
Promise.all()は、引数に指定した全てのPromiseを実行するメソッドです。全てのPromiseが履行(fulfilled)になるか、または1つでも拒否(rejected)になった時点で処理が終了します。
https://lab.syncer.jp/Web/JavaScript/Reference/Global_Object/Promise/all/
・promises.push(コールバック関数)って何?
promisesに(コールバック関数)のリターンを配列として入れるメソッドぽい。
これでpromisesに配列を貯めていって、最後promise.all()で吐き出してるってことか。
・async、awaitってなに?
この記事の後半に書いてました。
https://qiita.com/ef81sp/items/29b7b91a3f3e77cf2233#async--await
async / await は、Promise のシンタックスシュガーです。
■async
async は、(無名)関数の前に書くことで、async function という特別な関数を定義します。
以下の特徴を持ちます。
・ステートメント内で await 演算子が使える (後述)
・(暗黙的に) Promise を返す
・素の Promise のコンストラクターはなかなか独特ですが、asyncを使うと簡単に書けます。
・return の内容が、Promise のthenのコールバック関数に渡る
・throw すると、catchのコールバック関数に渡る
// 例; async functionなのでPromiseを返す
const order = async mealName => {
console.log(`オーダー: ${mealName}`);
if (soup.exists()) {
// スープがあったら
return cook(mealName); // 作ってreturn → thenのコールバックに渡る
} else {
throw new Error('スープないぜ'); // catchのコールバックに渡る
}
};
■await
await は、async functionの中でawait <Promise>と書くことで、
非同期処理の同期処理的な記述を実現します。
(あくまでもシンタックスシュガーなので、実際に扱っているのは非同期処理である Promise です)
以下の特徴を持ちます。
・await の後ろに Promise を置くことで、Promise の実行完了をその場で待つ
・処理に成功した場合、then のコールバックに渡される内容を返す
・async function の中でしか使えない (#FIXME!)
・2019/03/17 現在、トップレベルでawaitを使えるようにすることがECMAScript の仕様として検討されています。
先ほどの全マシを注文して、全マシを受け取る
写真屋で全マシと一緒に写真を撮って現像が終わったら写真を受け取る
SNS に上げていいねされる
ガッツポーズする
という処理が、以下のように書けます。
const doAsync = async () => {
const ramen = await order('全マシ'); // 全マシができるまで待つ
const photos = await takePhotos(ramen); // 写真が現像できるまで待つ
const like = await uploadThePhotosToSnsAndWaitForLike(photos); // いいねが来るまで待つ
doGutsPose();
};
よろしければサポートお願いします! 頂いたサポートはクリエイター活動に活用させて頂きます。