Edited at

GASを使って天気予報botを作ってみよう!


はじめに


どんな記事?

高校一年生が送る、高校生でも作れるLINEbot講座


こんなものを作ります



郵便番号を送信したらその地域の天気予報をFlexMessageで返してくれるbotです!


今回使うもの

・LINE Messaging API

・GAS(GoogleAppsScript)

・OpenWeatherMapAPI


まずはLINEBotを作ってみよう!

こちらのページにアクセスしてログインをしてください(メアドとパスワードはLINEのアプリで設定したものです)

②既にLINEbot作ったことがある方は既存のプロパイダーを、初めての人は新規プロパイダーを選択してプロパイダー名(テキトーに好きな名前)を入力し、プロパイダーの作成を完了させてください

③新規プロパイダーを制作した方はチャネル作成画面が出てくるので、Messaging APIを選択してください

④画像以外は必須項目なので、全て入力して「入力内容を確認する」を押してください

※この際にDeveloper Trialプランを選択すると最大友だち数が50人になってしまいますのでご注意ください

フリープランとDeveloper Trialプランの違いについてはこちらのページがとても分かりやすかったです

⑤確認画面が出てくるので、入力内容に間違いがなければ利用規約に同意するにチェックを入れて「作成」を押してください

⑥チャネル一覧が出てくるので、今作ったチャネルを選択してください

⑦下の方にスクロールすると「アクセストークン(ロングターム)」という項目が出てくるので、「再発行」を押してください(0時間でおk)

※出てきた文字列は後で使うので、コピペするなりしてどこかに保管しておいてください


OpenWeatherMapの設定

こちらのページにアクセスして、必要事項を入力し、アカウントを作成してください

こちらのページにアクセスして、APIKeyをコピーしてください(APIKeyがない場合は発行してください)


次はGASでプロジェクトを作ってみよう!

こちらのページにアクセスして「Start Scripting」を押してください

②スクリプトの編集ページが出てくるので、左上の「無題のプロジェクト」を押し、好きな名前に変更してください

③書いてあるコードを全部消して、以下のコードをコピペしてください

https://github.com/shinbunbun/Weather-bot/blob/master/%E3%82%B3%E3%83%BC%E3%83%89.js

④ペーストしたコードの1行目の「さっき発行したとーくん」をさっき発行したLINEのアクセストークンに、403行目の「さっき発行したえーぴーあいきー」をさっき発行したOpenWeatherMAPのAPIKeyに置き換えてください

④上の「公開」ボタンを押して、「ウェブアプリケーションとして導入」→「アプリケーションにアクセスできるユーザー」を「全員(匿名ユーザーを含む)」にして「導入」ボタンを押してください

⑤承認を求められることがあるので「許可を確認」を押して色々やって許可をしてください

④ウェブアプリケーションのURLが出てくるので、コピーして先ほどのLINEbotのページの「Webhook URL」というところにペーストしてください

⑤「webhook送信」を利用するにチェックを入れてください

これで完成です!!郵便番号を送信したらFlexMessageで天気が返ってくるはずです!


コード解説


doPost関数

function doPost(e) {

const events = JSON.parse(e.postData.contents).events;
events.forEach(function(event) {
switch (event.type) {
case "message":
reply(event);
break;
//postbackイベントの時はここに入る
/*case "postback" :
postback(event);
break;*/

}
});
}

GASではPOSTが飛んでくるとdoPost関数が呼ばれます。(ちなみにGETの場合はdoGetが呼ばれます)

doPost関数の中では、飛んできたJSONをパースしてeventオブジェクトを取り出し、イベントタイプごとに関数を振り分けるという処理をしています。


reply関数(前半)

function reply(e) {

var userMessage = e.message.text;
var response = getWeather(userMessage);
if (response != "error") {
var country = response.city.country,
cityName = response.city.name;
var date = [],
weather = [],
icon = [],
temperature = [];
for (var i = 0; i <= 8; i++) {
if (Number(response.list[i].dt_txt.slice(11, 13)) + 9 > 24) {
date.push(Number(response.list[i].dt_txt.slice(11, 13)) + 9 - 24);
}
else {
date.push(Number(response.list[i].dt_txt.slice(11, 13)) + 9);
}
weather.push(response.list[i].weather[0].main);
icon.push(response.list[i].weather[0].icon);
temperature.push((Math.round((Number(response.list[i].main.temp) - 273.15) * 10) / 10).toString() + '');
}

reply関数の前半では、ユーザーから送られてきたメッセージ(郵便番号)を引数としてgetWeather関数を呼び出し、天気予報を取得しています。その地域の天気予報のJSONを取得し、getWeatherの戻り値がerrorじゃなかった場合は、必要なデータ(時間、天気、アイコンの画像、温度)を取り出しています。その後にデータごとに配列に格納しています。


getWeather関数

function getWeather(e) {

try {
var apiKey = 'さっき発行したえーぴーあいきー';
var url = 'http://api.openweathermap.org/data/2.5/forecast' + '?zip=' + e + ',jp&APPID=' + apiKey;
var response = UrlFetchApp.fetch(url);
return JSON.parse(response);
Logger.log(response.list[0])
} catch (e) {
return "error";
}
}

reply関数の後半の前にgetWeather関数の説明をします。

getWeather関数では、OpenWeatherMapAPIを叩いて天気予報を取得する処理をしています。

郵便番号とAPIKEYをパラメータにしてAPIを叩き、レスポンスがエラーの場合(メッセージが郵便番号でなかった場合)は、念のためエラー内容を出力してから、"error"という戻り値を返します。

※APIの利用制限に引っかかった場合などもエラーが返ってくるため、念のためエラー内容を出力しておく。

存在する郵便番号かどうかを判別する処理を間に入れればこんなめんどくさいことしなくて済むのに


reply関数(後半)

        var message = {

"replyToken": e.replyToken,
"messages": [{
"type": "flex",
"altText": '天気予報',
"contents": {
"type": "bubble",
"styles": {
"footer": {
"separator": true
}
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "天気予報",
"weight": "bold",
"size": "xxl",
"margin": "md"
},
{
"type": "text",
"text": country + '.' + cityName,
"size": "md",
"color": "#aaaaaa",
"wrap": true
},
{
"type": "separator",
"margin": "xxl"
},
{
"type": "box",
"layout": "vertical",
"margin": "xxl",
"spacing": "sm",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[0] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[0],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[0] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[0],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[1] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[1],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[1] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[1],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[2] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[2],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[2] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[2],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[3] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[3],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[3] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[3],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[4] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[4],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[4] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[4],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[5] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[5],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[5] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[5],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[6] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[6],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[6] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[6],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[7] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[7],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[7] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[7],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": date[8] + ":00",
"size": "sm",
"color": "#555555",
"flex": 0
},
{
"type": "text",
"text": weather[8],
"size": "sm",
"color": "#111111",
"align": "end"
},
{
"type": "icon",
"url": "https://openweathermap.org/img/w/" + icon[8] + ".png",
"size": "xl"
},
{
"type": "text",
"text": temperature[8],
"size": "sm",
"color": "#111111",
"align": "end"
}
]
}
]
},
{
"type": "separator",
"margin": "xxl"
}
]
}
}
}]
};
} else {
var message = {
"replyToken": e.replyToken,
"messages": [{
"type": "text",
"text": "その郵便番号は存在しておりません"
}]
};
}
var replyData = {
"method": "post",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer " + access_token
},
"payload": JSON.stringify(message)
};
try {
UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", replyData);
} catch (e) {

}
}


reply関数の後半では、まずgetWeatherの戻り値が"error"でなかった場合に送信するFlexMessageを作っています。詳しくはこちらのリファレンスを参照。

その次にgetWeatherの戻り値が"error"だった場合(郵便番号が存在しなかった場合)に送信するメッセージを作っています。

最後に出来上がったメッセージをLINEのサーバにPOSTで飛ばして終了です!


宣伝


時間割bot

僕が作っている学生向けのサービス。利用者数は680人(2018/11/29現在)。

友だち追加



Twitter

https://twitter.com/shinbunbun_