#はじめに
「出先で雨に降られ、濡れがち」
「物忘れ多すぎ」
「部屋に籠ってばっかで寂しい」
ここ最近の個人的問題を一気に解決すべく、GAS(GoogleAppsScript)を用いてLINEbot作成しました。
#作ったもの
「天気予報を朝にプッシュ通知で知らせる」「指定時間にリマインド」の二つの機能を、「好きなキャラとのLINE風」に実装することでウキウキな気分になれます。
ちなみに私の好きなキャラは、シャニマスの黛冬優子さん。
黛 冬優子(まゆずみ ふゆこ)|アイドルマスターシャイニーカラーズ(シャニマス)
#実装
##1. 準備
Google Apps Scriptでオウム返しLINE Botを作る。
こちらの記事を参考にbot用のLINEアカウントを作成し、自分の発言をオウム返しするプログラムを実装してみましょう。
GASであれば、サーバーを用意せず無料でLINEbotを動かせます。
##2. 天気予報の通知
LINE BOT お天気通知くん
こちらの記事を参考にし、天気予報、気温、降水確率、傘が必要か、を通知してくれる関数を作ります。
var CHANNEL_ACCESS_TOKEN = 'アクセストークンを入力';
function RandomReturn(){
if (Math.floor(Math.random()*100)<5){
return true;
}
return false;
}
function push_message() {
var today = new Date();
var toWeekday = toWD(today);
var msgWeatherForecast = getTemperatureForecast();
var postData = {
"to": 'ユーザーIDを入力',
"messages": [
{
"type": "text",
"text": "おはようございます!" +Utilities.formatDate( today, 'Asia/Tokyo', 'M月d日') + toWeekday + "、今日の天気をお知らせしますね♡\n"
+ msgWeatherForecast[0] + msgWeatherForecast[1] + msgWeatherForecast[2]
},{
"type": "text",
"text": msgWeatherForecast[3]
}
]}
var headers = {
"Content-Type": "application/json",
'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
};
var options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(postData)
};
var response = UrlFetchApp.fetch("https://api.line.me/v2/bot/message/push", options);
}
// 天気予報の取得
function getTemperatureForecast() {
const area = "東京地方"
var options =
{
"contentType" : "text/xml;charset=utf-8",
"method" : "get",
};
var response = UrlFetchApp.fetch("https://www.drk7.jp/weather/xml/13.xml", options);
var xmlDoc = XmlService.parse(response.getContentText());
var rootDoc = xmlDoc.getRootElement();
var region = parser.getElementById(rootDoc,area);
var weather = parser.getElementsByTagName(region, 'weather');
var temperature = parser.getElementsByTagName(region, 'range');
var rainyPercent = parser.getElementsByTagName(region, 'period');
var weathermsg = "■天気予報" + "\n" + weather[0].getValue() + "\n";
var tempmsg ="■気温\n" + temperature[0].getValue() + "℃/" + temperature[1].getValue() + "℃\n";
var rainfall = "■降水確率\n"+"朝:"+rainyPercent[1].getValue()+"%"+"昼:"+rainyPercent[2].getValue()+"%"+"夜:"+rainyPercent[3].getValue()+"%";
var umbrellamsg = getUmbrellNecessary(rainyPercent[1].getValue(),rainyPercent[2].getValue(),rainyPercent[3].getValue()) ;
var rainyTemperature = [weathermsg,tempmsg,rainfall,umbrellamsg];
return rainyTemperature
}
// 傘予想
function getUmbrellNecessary(mor,eve,nig){
var msg = ""
if(mor > 70 || eve > 70 || nig > 70){
if (nig > 70 ) {
msg = "夜降るらしいわよ。傘持っていきなさい";
}
if (eve > 70){
msg = "昼から降るみたいよ。傘持っていきなさい";
}
if (mor > 70 ) {
msg = "ちょっと......朝から雨?";
}
}else if(mor <= 70 && mor > 20 || eve <= 70 && eve > 20 || nig <= 70 && nig > 20 ){
msg = "雨降ってんじゃないの?\n今降ってなくても、折りたたみ持っていきなさいよ";
}else if(mor <= 20 && mor > 10 || eve <= 20 && eve > 10 || nig <= 20 && nig > 10){
msg = "傘は必要なさそうね";
}else if(mor <=10 && eve <=10 & nig <=10) {
msg = "ふゆが晴れにしてあげたのよ、感謝しなさい!";
}else{
msg = "たまには自分で調べなさい";
}
if(RandomReturn()){
msg = "ふゆにばっか頼ってないでたまには自分で調べたら?";
}
return msg
}
// 曜日の日本語変換
function toWD(date){
var myTbl = new Array("日","月","火","水","木","金","土","日");
var myDay = Utilities.formatDate(date, "JST", "u");
return "(" + myTbl[myDay] + ")";
}
完全なプライベート用なので、ユーザーIDを指定して送信します。
pushMessage()を毎朝6時に実行するようtriggerを設定し、起きた時通知が入ってるようにします。
また、キャラの性格を反映させるべく、5%の確率で傘予想を教えてくれない仕様にしています。気持ち悪いこだわり。
##3. リマインド
LINEで予定を登録→通知してくれるリマインダーアプリを作ろう
こちらの記事を参考に、一人のユーザーが複数のタスクを登録・リマインドできるよう変更を加え、実装します。
###3.1 スプレッドシートの操作に関する関数
var spreadsheet = SpreadsheetApp.openById('IDを入力');
var sheet = spreadsheet.getSheetByName('webhook');
function appendToSheet(text) {
sheet.appendRow([text]);
}
//セルに中身があるか
function blankTF(row, col){
return !(sheet.getRange(row, col).isBlank());
}
//sheetの行数
function lastRow() {
var dat = sheet.getLastRow();
return dat;
}
//全行サーチ
function seachAll(){
var dat = sheet.getDataRange().getValues();
}
//指定した行の削除
function deleatRow(RowNum){
sheet.deleteRow(RowNum);
}
//全削除
function deleatAll(){
sheet.clearContents();
}
![S__70885388.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378758/8c023ddc-0b07-e307-8a6f-ba3b6387d06f.jpeg)
//セル[row][col]の値を返す
function getFromRowCol(row, col) {
var dat = sheet.getDataRange().getValues();
return dat[row][col];
}
//セル[row][col]にvalを入れる
function setFromRowCol(val, row, col) {
sheet.getRange(row , col).setValue(val);
}
//row行1列のデータ(内容)を取得
function getTodoCell(row) {
return sheet.getRange(row , 1).getValue();
}
//row行2列のデータ(日付)を取得
function getDateCell(row) {
return sheet.getRange(row , 2).getValue();
}
//row行3列のデータ(trigger)を取得
function getTriggerCell(row) {
return sheet.getRange(row , 3).getValue();
}
スプレッドシート使って、リマインドしたいタスクを管理しています。
1列目にリマインド内容、2列目にリマインドする日付、3列目にtriggerIDを格納します。
GASには指定したタイミングで関数を実行するtriggerという機能があり、セットした時に割り振られる固有のIDがtriggerIDです。
###3.2 Triggerをセット、削除する関数
function setTrigger(row, date) {
var triggerDay = moment(date,'YYYY年MM月DD日H時m分').toDate();
var trigger = ScriptApp.newTrigger("remind").timeBased().at(triggerDay).create();
setFromRowCol(trigger.getUniqueId(), row, 3);
}
function deleteTrigger(uniqueId) {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getUniqueId() === uniqueId) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
ここは参考記事まんまです。
setTriggerは、格納された日時になると関数を呼び出すtriggerをセットします。
##3.3 リマインド本体
var moment = Moment.load();
//ポストで送られてくるので、送られてきたJSONをパース
function doPost(e) {
var json = JSON.parse(e.postData.contents);
var replyText = 'null';
var filledDo = false;
var filledDate = true;
if(lastRow()>0){
filledDo = blankTF(lastRow(),1);
filledDate = blankTF(lastRow(),2);
}
var userId = json.events[0].source.userId;
//返信するためのトークン取得
var replyToken= json.events[0].replyToken;
if (typeof replyToken === 'undefined') {
return;
}
var message = json.events[0].message.text;
switch (message) {
case 'キャンセル':
replyText = cancel();
break;
case '天気':
replyText = '';
push_message();
break;
case '確認':
if (lastRow()>0) {
replyText = confirm();
} else {
replyText = 'まだ何にも登録してないでしょ';
}
break;
default:
if(message.match(/削除/)){
replyText = del(message);
}
else if(filledDate){
replyText = setDo(message);
break;
}else{
replyText = setDate(message);
break;
}
break;
}
// メッセージを返信
UrlFetchApp.fetch(line_endpoint, {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
'type': 'text',
'text': replyText,
}],
}),
});
return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}
//データの削除
function del(m){
m=m.replace('削除', '');
if (m==''){
return '「削除+リマインダー番号」で指定しなさい'
}
m = m.replace(/[A-Za-z0-9]/g, function (s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
});
if(m>lastRow()){
return '番号間違ってるわよ\nもう一回確認しなさい';
}
var s = getTodoCell(m);
var deltriggerID = getTriggerCell(m);
deleteTrigger(deltriggerID);
deleatRow(m);
return '「'+s+'」'+'を削除したわ';
}
//データの確認
function confirm(){
var replyText = 'まだ何にも登録してないでしょ';
if(lastRow()>0){
replyText = '';
for (var i = 1; i <= lastRow(); i++) {
replyText += i+';'+ getDateCell(i) + 'に「' + getTodoCell(i) + '」\n';
}
}
replyText+='以上よ';
return replyText;
}
//入力のキャンセル
function cancel(){
if(lastRow()>0){
deleatRow(lastRow());
return 'キャンセルしたわ\nまたなんかあったら言って';
}else{
return 'キャンセルするものなんてないわよ';
}
}
//日付の入力
function setDate(m){
// 全角英数を半角に変換
m = m.replace(/[A-Za-z0-9]/g, function (s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
});
var date = Moment.moment(m, 'M月D日H時m分', true).format('YYYY年MM月DD日H時m分');
if (date === 'Invalid date') {
var match = m.match(/\d+/g);
if (match !== null) {
date = Moment.moment().add(+match[0], 'minutes').format('YYYY年MM月DD日H時m分');
}
}
if (date === 'Invalid date') {
return '「何分後」「」'
}
setTrigger(lastRow(), date);
setFromRowCol(date,lastRow(), 2);
return 'はいはい\n'+date + 'に連絡するわ';
}
//データの入力
function setDo(m){
if(RandomReturn()){
return 'めんどくさぁ......';
}
setFromRowCol(m, lastRow()+1, 1);
return '「' + m + '」ね、いつ連絡すればいいのよ\n「キャンセル」って言ってくれればやめるけど';
}
function getnowDate(){
return Moment.moment().format('YYYY年MM月DD日H時m分');
}
function remind(e){
var nowDate = getnowDate();
var replyText = 'リマインド登録されてないわよ';
if(lastRow()>0){
for (var i = 1; i <= lastRow(); i++) {
replyText = getDateCell(i);
if(getDateCell(i)==Moment.moment().format('YYYY年MM月DD日H時m分')){
replyText = getTodoCell(i);
var deltriggerID = getTriggerCell(i);
deleteTrigger(deltriggerID);
deleatRow(i);
}
}
}
var postData = {
"to": 'ユーザーID',
"messages": [
{
"type": "text",
"text": replyText+'\nふゆがわざわざリマインドしてあげたんだから、忘れるんじゃないわよ'
}
]}
var headers = {
"Content-Type": "application/json",
'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
};
var options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(postData)
};
var response = UrlFetchApp.fetch("https://api.line.me/v2/bot/message/push", options);
}
送られてきたテキスト内容によって、天気予報のwether()、タスクを削除するdel()、登録してあるタスクを確認するconfirm()、現在の入力をキャンセルするcancel()、リマインドする日時を格納するsetDate()、リマインドするタスクを格納するsetDo()のどれかにシフトするようになっています。
ここでもタスクを登録する際、5%の確率で面倒くさがられます。