[続編] Line Bot＋GAS＋ペーパー液晶でLineからリマインダーを送って情報表示額縁に表示させた

# はじめに

[前回の記事『「電子ペーパーを額装して飾るとやたらカッコいい::デイリーポータルZ」を参考にラズパイ＋GAS＋ペーパー液晶で情報表示額縁を作った』](https://qiita.com/nokoribi/items/5045831420004d71bde9)で情報表示額縁を作成し、天気予報などの情報を表示させました。

今回は、Lineからリマインダーをトーク送信して、情報表示額縁に表示させるようにしました。

## リマインダーの表示例

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/825d4d9a-e31d-226c-6d8f-1036dded3bf8.jpeg' width='50%'/>

（「by」の後に名前を表示していますが、上記写真ではカットしています）

## Lineからの操作例

![line_bot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/7d8c36a0-ece6-8029-ae4e-bd5d8b923dc6.png)

Line Botにリマインダーテキストを送信すると、リマインダーに追加されます。登録されているリマインダーリストを返してきます。

「数字」だけを入力すると、上記リスト中の指定番号のものを削除できます。


# 全体の処理の流れ

（１）LineアプリからリマインダーのテキストをLine Botに送信する。
（２）Line BotがGoogle App Script経由でGoogle SpredSheetにリマインダーテキストを追加する。
（３）Google App Scriptで上記のリマインダーテキストや天気予報情報を取得して、HTMLの形で返す。
（４）ラズベリーパイで上記HTMLデータをphantomjsで読み込み、画像化する。
（５）上記画像をimagemagickで二値画像のBMPファイルに変換する。
（６）二値画像のBMPファイルをラズベリーパイに接続したペーパー液晶で表示させる。
（７）上記の（３）から（６）までの処理を一定時間毎に実行させる。

（３）から（７）までの処理は[前回の記事](https://qiita.com/nokoribi/items/5045831420004d71bde9)でご紹介したので省略し、今回は（１）から（３）までの処理をご説明いたしました。

# リマインダーのGoogle SpreadSheetの準備

Google DriveからGoogle SpreadSheetを作成します。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/ee4d8a22-8798-763f-b547-8a7f9348163c.png' width='80%'>

上記のように１行目はタイトル行とし、A列をリマインダーのテキスト、B列を作成日時、C列を作成者、D列を期限日時、としています。

URLが https://docs.google.com/spreadsheets/d/XXXXXXXXXXXXXXXXXXXX/edit#gid=0 などとなっていると思いますので、「XXXXXXXXXXXXXXXXXXXX」の部分をSpreadSheetのIDとして控えておきます。


# SpreadSheetのリマインダーテキストを取得／削除するGoogle App Scriptを作成する

（１）Google DriveからGoogle App Script「line_bot.gas」を下記で作成します。

```javascript:line_bot.gas：
var CHANNEL_ACCESS_TOKEN = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'; 
var line_endpoint = 'https://api.line.me/v2/bot/message/reply';

function addMemo( memo, owner, expiredHours ) {
  
  var spreadsheet = SpreadsheetApp.openById('XXXXXXXXXXXXXXXXXXXX');
  var sheet = spreadsheet.getActiveSheet();

  let currentDate = new Date();
  
  let createdDateStr = Utilities.formatDate(currentDate, 'JST', 'yyyy-MM-dd HH:mm');
  currentDate.setHours(currentDate.getHours() + expiredHours);
  let expiredDateStr = Utilities.formatDate(currentDate, 'JST', 'yyyy-MM-dd HH:mm');
    
  sheet.appendRow([memo, createdDateStr, owner,expiredDateStr ]);

  let list = '';  
  const lastRow = sheet.getLastRow();
  for(let i = 2; i <= lastRow; i++) {    
    list += "\n　" + (i-1) + ':' + sheet.getRange(i, 1).getValue();
  }

  return list;
}


function sendMail( message ) {

  var today = Utilities.formatDate(new Date(), 'JST', 'yyyy年M月d日 H時m分s秒');

  var MailTitle = message;

  // 本文は空とする
  GmailApp.sendEmail('foo@gmail.com', MailTitle, '')
}


function doPost(e) {
  var json = JSON.parse(e.postData.contents);
  
  Logger.log( 'json = ' + json );

  //返信するためのトークン取得
  var reply_token= json.events[0].replyToken;
  if (typeof reply_token === 'undefined') {
    return;
  }

  //送られたメッセージ内容を取得
  var message = json.events[0].message.text;  
  
  let resStr = '';
  
  if ( isNaN( message ) ) {
    // 数字以外が含まれるので追加命令

    //送信したユーザ名を取得
    var owner = '誰か';  
    var userId = json.events[0].source.userId;

    // Lineのユーザー名を取得するのが面倒なので、userIdとの対応でハードコーディング
    if ( userId == 'ZZZZZZZZ' ) {
      owner = 'MyName';
      sendMail( message );
    } else {
      message += ' (' + userId + ') ';
    }

    let list = addMemo( message, owner, 24 );
    
    resStr = '「' + message + "」を追加しました。" + list;
  
  } else {
    // 数字のみなので削除命令
    var spreadsheet = SpreadsheetApp.openById('XXXXXXXXXXXXXXXXXXXX');
    var sheet = spreadsheet.getActiveSheet();
    
    let deleteRowIndex = Number(message) + 1;
    
    let deleteMemo = sheet.getRange(deleteRowIndex, 1).getValue();
    
    sheet.deleteRows(deleteRowIndex);

    let list = '';  
    const lastRow = sheet.getLastRow();
    for(let i = 2; i <= lastRow; i++) {    
      list += "\n　" + (i-1) + ':' + sheet.getRange(i, 1).getValue();
    }

    resStr = '「' + deleteMemo + "」を削除しました。" + list;
  }
  
  // メッセージを返信    
  UrlFetchApp.fetch(line_endpoint, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': [{
        'type': 'text',
        'text': resStr,
      }],
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}
```

「YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY」はLineのチャネルアクセストークン、「XXXXXXXXXXXXXXXXXXXX」は上記のSpreadSheetのIDに差し替えてください。

（２）メニューの「公開」／「ウェブアプリケーションとして導入...」を選択します。

（３）「Who has access to the app:」を「Anyone, even anonymous」にします。（URLを知っていると誰でもアクセスできるのでご注意ください。自分のアカウントの権限で実行するので、個人情報などをスクリプトで出力しないようにご注意ください）。

（４）「Deploy」ボタンを押します。

（５）「Authorization Requied」というダイアログがでますので、「許可を確認」ボタンを押します。

（６）GoogleのAuth認証ダイアログがでますので、自分のアカウントを選択します。

（７）「このアプリは確認されていません」というダイアログが出ますので、下の「xxxxxxx（安全ではないページ）に移動」のリンクを押します。（「xxxxxxxx」は（１）のプロジェクト名）

（８）「xxxxxxxxが Google アカウントへのアクセスをリクエストしています」というダイアログが出ますので、下の「許可ボタン」を押します。

（９）「Current web app URL」のURLを控えておきます。これがLine Botに与えるwebhookのURLとなります。


上記のスクリプリでは、リマインダーの追加、リマインダーの削除、メールの送信の機能を入れています。

リマインダー

# Line Botの作成

## Line開発者アカウントの設定とBOTの作成

Lineの開発者アカウント登録とMessaging APIの設定と簡単なLine Botの作成は下記を参考にしてください。

* [【LINE BOT】Messaging APIとGASでLINE BOTを作ってみた](https://qiita.com/gaosan/items/6ff75663b58ab133ef97)

* [Google Apps Scriptでオウム返しLINE Botを作る。](https://qiita.com/tetrapod117/items/e7b48485c98f6b88f311)

## チャネルアクセストークンの取得

Lineの「Messaging API設定」タブで「チャネルアクセストークン（長期）」の「発行」ボタンを押して、チャネルアクセストークンを控えておきます。

チャネルアクセストークンは上記のGoogle App Scriptの「YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY」の部分に差し替えておきます。


## BotのWebhookを設定する

Lineの「Messaging API設定」タブで、上記のGoogle App Script「line_bot.gas」の（９）のURLをWebhookのURLとして設定します。「Webhookの利用」も有効にしておきます。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/7c0d4df9-6c6a-f4a5-2cf3-c65dbdc83b57.png' width='80%'/>


# 情報表示額縁用のリマインダーHTMLの作成

[前回の記事](https://qiita.com/nokoribi/items/5045831420004d71bde9)で「Google App Scriptを使った情報表示用Webページの作成」のGoogle App Scriptに下記関数を追加します。

```javascript:
function getReminderHtmlStr() {
  
  let htmlStr = "";
  
  var spreadsheet = SpreadsheetApp.openById('XXXXXXXXXXXXXXXXXXXX');
  var sheet = spreadsheet.getActiveSheet();
  
  let currentDate = new Date();
  
  var lastRow = sheet.getLastRow();
  for(let i = lastRow; 2 <= i ; i--) {
    let memo = sheet.getRange(i, 1).getValue();
    let createdDate = sheet.getRange(i, 2).getValue();
    let owner = sheet.getRange(i, 3).getValue();
    let expireDate = sheet.getRange(i, 4).getValue();
    let isExpired = expireDate < currentDate;
    Logger.log( memo + ", " + createdDate + ", " + owner + ", " + expireDate + ", " + isExpired );
    if ( isExpired == false ) {
      htmlStr += '<div class="memo_line center"><span class="memo">' + memo + '</span><span class="memo_owner"> by ' + owner + '</span></div>';
    } else {
      sheet.deleteRows(i);
    }
  }
  
  return htmlStr;
}
```

期限日時を超えるリマインダーの行は削除するようにしています。

doGet()から上記関数を呼び出して、情報を追加するようにします。

```javascript:
function doGet() {

  // 中略

  let memoHtmlStr = getMemoHtmlStr();
  if ( memoHtmlStr != '' ) {
    s += memoHtmlStr;
  }

  // 中略

  return HtmlService.createHtmlOutput(s);

```

# Lineからリマインダーの操作例

![line_bot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/7d8c36a0-ece6-8029-ae4e-bd5d8b923dc6.png)

（１）Line Botにリマインダーテキストとして「傘を乾かす」を送信します。

（２）Line Botから『「傘を乾かす」を追加しました。1:傘を乾かす』が返ってきます。

この例では１個しかありませんが、複数あるときは「1:xxxx, 2:xxxx」としてリストが返ってきます。

送信された各リマインダーは、24時間で自動的に削除されます。

（３）リマインダーを即座に削除したい場合はリスト中の番号を送信します。

# 所感

雨の時は折りたたみ傘をカバンに入れていますが、家に着いてから乾かすのを忘れたりすることが時々あります。他にも、次の日に何かを持っていかないといけないことを朝に忘れてしまうことがあります。

従来はGoogle Keepに追加したり、自分宛にリマインダーをメールしたりしていたのですが、家に着いてからGoogle Keepやメールを見ないこともあるので効果が今ひとつでした。

情報表示額縁はリビングに常においてあり、帰った時や朝起きた時などに必ず目に入りますので、忘れることがなくなりました。