これでもう見逃さない！Amazonアウトレットのお得情報をTwitterからLINE Botに自動連携。

##Amazonアウトレットのお得情報は見逃せない！
Amazonアウトレットというサービスをご存知でしょうか？
Amazonでは以下のように説明されていますが、とにかく**お得に商品を購入できるサービス**です。

>Amazonアウトレットは、倉庫内で梱包に傷を負った商品・お客様から返品された商品のうち、商品の状態が良いにも関わらず、Amazon.co.jp の厳密な基準では新品として販売しないものを「アウトレット品」という扱いで、お手頃な価格で販売しています。また、倉庫内で保管され、賞味期限が近づいた食品を、お手頃な価格で販売しています。

さて、とてもお得なAmazonアウトレットですが、問題点が一つ。
**商品が登録されてから売り切れるまでが非常に早い**のです。（モノによりますが）
私はTwitterでAmazonアウトレットのお得情報をツイートしてくれるアカウントをフォローしていますが、**気づいて見に行った時にはもう売り切れている**ということが良くありますし、そもそも**他のツイートに埋もれて気づかない**ということも間々あります。

そこで、Twitterで発信されたお得情報を素早くキャッチするため、LINE Botで通知をもらうようにしました。
ついでにGoogleスプレッドシートで一覧化すれば商品の検索もスムーズになるってもんです。

##作ったもの
以下の様なサービスを組み合わせて自動化の仕組みを作りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/c06da2ec-b033-3adc-5ffa-c97224a5bce4.png)


![Screenshot_20210529-152122_LINE.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/e1281b16-7b2f-6d37-30dc-eefcb125f771.jpeg)
こんな感じでLINEに飛んできます。

それぞれの役割と使用理由について簡単に書きます。

**①Twitter**
Amazonアウトレットのお得情報を拾うための媒体です。
今回は**特定のアカウントのツイートを監視**することになります。

**②Googleスプレッドシート**
Twitter上で発信された情報を一覧化するために使用。
**リンクなどを見やすく一覧化**することでAmazonアウトレットでのお買い物が捗るはずです。

**③LINE Bot**
Twitter上でお得情報が発信されたことを私に気づかせてくれます。
**「結局LINEが一番タイムリーに確認する（できる）」**ということで採用

**④Zapier**
ツイートに反応してGoogle Apps Scriptに伝える役割を担います。
IFTTTと違いWebhook機能が有料だったり（今回は無償期間で作成）月の実行制限(100回/月)が厳しかったりしますが、
UIが使いやすかったのとテストのし易さから採用に至りました。

**⑤Google Apps Script（GAS）**
Zapierからのリクエストを受け付けてGoogleスプレッドシートに書き込みつつ、次のWebhookに繋ぐという役割。
スプレッドシートの取り回しが良いところが一番の利点でしょうか。

**⑥Integromat**
GASからのリクエストを受け付けてLINE Botにブロードキャストする役割になります。
Webhook利用が無料且つLINEとのコネクタが用意されているので採用。

##作り方
ここからは作り方を解説していきます。
今回の仕組みは連携の後ろ側から作った方がやりやすいので順番は以下の通りです。
**①LINE Botで通知する部分**
**②情報をスプレッドシートに書き込む部分**
**③ツイートに反応してWebhookを投げる部分**

###①LINE Botで通知する部分
最終的にLINE Botで通知を受け取りたいのですが、通知の内容はツイートの本文にしようと思います。
この部分は既に記事にしてあるので、こちらを参考にしてください。（リンク先の内容がそのまま使えます。）

[Integromatを使ってWebhookで受け取った内容をLINE Botに流す](https://qiita.com/shoito66/items/2f67e4de549dd844c0f4)

###②情報をスプレッドシートに書き込む部分
続いてGAS上で作る部分です。
自身のGoogleアカウントで新規スプレッドシートを開きましょう。
Googleのホーム画面でアプリ一覧を選択して真ん中あたりにあるスプレッドシートを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/5966a100-5048-6398-80e6-dc7ee193ca90.png)

スプレッドシートの管理画面に飛んだら空白のスプレッドシートを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/6ac48910-4aeb-226f-101b-f74268a08cd9.png)

スプレッドシートが開いたら「ツール」⇒「スクリプトエディタ」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/9bb36fb8-0da1-1f45-ed7a-b6fec1be3582.png)

エディタが開くので、ここにスクリプトを書き込んでいきます。

<details><summary>今回作ったスクリプトの全文はこちら</summary><div>

```JavaScript
// POSTリクエストを処理する関数
function doPost(e) {
  // textというキーに値があればそれをスプレッドシートに書き込みます（FormData）
  let params = JSON.parse(e.postData.getDataAsString());
  writeLogToSheet(params.create,params.text,params.url1,params.url2);

  //Integromatで用意したWebhookURLにテキストを投げる
  const URL = 'Integromatで作成したWebhookのURL(末尾にパラメータ用の「?text=」を付ける)';
  UrlFetchApp.fetch(URL + encodeURIComponent(params.text));

  // リクエスト送信元に返信する
  const data = { text: `POSTリクエストを受付ました。` };
  return ContentService.createTextOutput(JSON.stringify(data));
}

// シートにログを書き込む関数
function writeLogToSheet(create = 'x',text = 'x',url1 = 'x',url2 = 'x') {
  // シート取得
  const sheet = SpreadsheetApp.getActiveSheet();
  // ループを回して1行ずつすでに記録されていないか確認していく
  let currentRow = 1;
  while (true) {
    // 記録されていない行が見つかったとき：
    if (!sheet.getRange(currentRow, 1).getValue()) {
      // 1列目に日付時刻を記録
      sheet.getRange(currentRow, 1).setValue(create2date(create));
      // 2列目に本文テキスト（引数値）を記録
      sheet.getRange(currentRow, 2).setValue( text );
      //商品URLは","区切りなので分解
      let urls = url1.split(',');
      // 3列目に商品URL（分割した1つ目）を記録
      sheet.getRange(currentRow, 3).setValue( urls[0] );
      // 4列目に予備URL（分割した2つ目）を記録
      sheet.getRange(currentRow, 4).setValue( urls[1] );
      // 5列目にツイートURL（引数値）を記録
      sheet.getRange(currentRow, 5).setValue( url2 );
      // ループ中断
      break;
    }
    // 次の行へ
    currentRow++;
  }
}

//日付時刻を成型する関数
create2date = function(create){
	let date = new Date(create);
	date.setHours(date.getHours()); // UTC -> JST (+9時間)
	let y    = date.getFullYear();      // 年取得
	let mon  = date.getMonth() + 1;     // 月取得
	let day  = date.getDate();          // 日取得
	let h    = date.getHours();         // 時取得
	let min  = date.getMinutes();       // 分取得
  let sec  =  date.getSeconds();      // 秒取得

  //年月日時分秒に成型
	create = y+ '/' + mon + '/' + day +' ' + h + ':' + min + ':' + sec;
	return create;
}
```

</div></details>

作成したスクリプトは3つの関数でできています。
順番に見ていきましょう。

####POSTリクエストを処理する関数

```JavaScript
function doPost(e) {
  // textというキーに値があればそれをスプレッドシートに書き込みます（FormData）
  let params = JSON.parse(e.postData.getDataAsString());
  writeLogToSheet(params.create,params.text,params.url1,params.url2);

  //Integromatで用意したWebhookURLにテキストを投げる
  const URL = 'Integromatで作成したWebhookのURL(末尾にパラメータ用の「?text=」を付ける)';
  UrlFetchApp.fetch(URL + encodeURIComponent(params.text));

  // リクエスト送信元に返信する
  const data = { text: `POSTリクエストを受付ました。` };
  return ContentService.createTextOutput(JSON.stringify(data));
}
```

POSTリクエストを受け付けた際に動く関数です。

パラメータはJson形式に変換します。

```JavaScript
let params = JSON.parse(e.postData.getDataAsString());
```

スプレッドシートに書き込んだ後はLINE Botに繋げるために、先ほどの手順で作成したIntegromatのWebhookURLに対してGETリクエストを投げます。

```JavaScript
const URL = 'Integromatで作成したWebhookのURL(末尾にパラメータ用の「?text=」を付ける)';
UrlFetchApp.fetch(URL + encodeURIComponent(params.text));
```

####スプレッドシートへの書き込みを行う関数

```JavaScript
function writeLogToSheet(create = 'x',text = 'x',url1 = 'x',url2 = 'x') {
  // シート取得
  const sheet = SpreadsheetApp.getActiveSheet();
  // ループを回して1行ずつすでに記録されていないか確認していく
  let currentRow = 1;
  while (true) {
    // 記録されていない行が見つかったとき：
    if (!sheet.getRange(currentRow, 1).getValue()) {
      // 1列目に日付時刻を記録
      sheet.getRange(currentRow, 1).setValue(create2date(create));
      // 2列目にメッセージ（引数値）を記録
      sheet.getRange(currentRow, 2).setValue( text );
      //商品URLは","区切りなので分解
      let urls = url1.split(',');
      // 3列目に商品URL（分割した1つ目）を記録
      sheet.getRange(currentRow, 3).setValue( urls[0] );
      // 4列目に予備URL（分割した2つ目）を記録
      sheet.getRange(currentRow, 4).setValue( urls[1] );
      // 5列目にツイートURL（引数値）を記録
      sheet.getRange(currentRow, 5).setValue( url2 );
      // ループ中断
      break;
    }
    // 次の行へ
    currentRow++;
  }
}
```

先ほど新規作成したスプレッドシートを1行目から探索して、空行があったら書き込むようになっています。
POSTリクエストで受け取ったパラメータを1列目から順に書いていますが、「url1」だけはツイートの性質から2つURLが入ってくることが分かっているので、分割して記録しています。

```JavaScript
//商品URLは","区切りなので分解
let urls = url1.split(',');
// 3列目に商品URL（分割した1つ目）を記録
sheet.getRange(currentRow, 3).setValue( urls[0] );
// 4列目に予備URL（分割した2つ目）を記録
sheet.getRange(currentRow, 4).setValue( urls[1] );
```

####ツイート時刻の成型をする関数

```JavaScript
create2date = function(create){
	let date = new Date(create);
	date.setHours(date.getHours()); // UTC -> JST (+9時間)
	let y    = date.getFullYear();      // 年取得
	let mon  = date.getMonth() + 1;     // 月取得
	let day  = date.getDate();          // 日取得
	let h    = date.getHours();         // 時取得
	let min  = date.getMinutes();       // 分取得
  let sec  =  date.getSeconds();      // 秒取得

  //年月日時分秒に成型
	create = y+ '/' + mon + '/' + day +' ' + h + ':' + min + ':' + sec;
	return create;
}
```

TwitterのAPI仕様でツイート時刻のフォーマットが「Wed, 03 Oct 2012 00:00:00 +0000」のようになっています。
このままでは見づらいので変換をしています。

####作成したスクリプトをWebアプリケーションとしてデプロイ
スクリプトが完成したら保存した後にデプロイします。
画面右上のデプロイを選択ご、「新しいデプロイ」を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/0b205634-6154-a7a8-67de-cf842db3ec20.png)

種類の選択の右にある歯車マークを選択して「ウェブアプリ」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/0802235b-b2f9-4eec-e95f-ad0b372b000d.png)

アクセスできるユーザーを「全員」にしてデプロイを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/ec82c549-8027-269a-74c7-021f237d22bd.png)


ウェブアプリのURLが表示されます。（アカウントに対する許可を求められる場合があります。）
このURLは次の工程で使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/db1f8160-ea8e-8acc-48aa-1b5835547448.png)


これでGAS上での作業は完了です。

###③Twitterの呟きに反応してWebhookを投げる部分
Zapierで新しいZapを作ります。
Triggerに「Twitter」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/f3d02c57-f29a-85ee-44f5-ffd787710dbd.png)

今回は特定アカウントのツイートを監視するのでTrigger Eventは「User Tweet」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/aa875d48-48b8-164d-2cae-becc61c57270.png)

自身のアカウントで認証したら、Set up triggerに監視したいアカウントを入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/d44a6b85-3c79-3073-6749-01ccda768f36.png)

Test triggerで問題なくツイートが拾えればOKです。

続いてActionの設定です。
Webhooks by Zapierを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/26ee4967-5a91-f9ba-44bb-9d69c83e10a2.png)

Action EventはPOSTを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/be2eeadd-aa3d-8271-00a2-1e59b3fc2805.png)

URLにGAS上のWebhookURLを設定して、Payload Typeを「Json」に変更します。
パラメータとして以下の4つを設定します。

|パラメータ名 |設定したtweetの属性|概要          |
|:-----------|:-----------------|:-------------|
| create     | Created At       | ツイートされた時刻    |
| text       | Text             | ツイートの本文        |
| url1       | Entities Urls URL| ツイート中に記載の商品URL |
| url2       | URL              | ツイートのURL        |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264471/1cd5efe5-1998-07f3-84d9-78b0bbba6d4e.png)

これで設定は完了です。
テストを実行してみてください。
上手くいけばスプレッドシートへの書き込みとLINE Botからの通知が実行されます。

##まとめ
今回はTwitterを監視してLINE Botに繋ぎましたが、これが想像以上に便利でした。
**特定アカウントだけでなくツイートの中身によって通知することもできる**ので色々な使い道が出てきそうです。
ただ、今回使用したZapierは無料アカウントだとWebhookが使えなかったり、月の実行数制限が厳しかったりするので、IFTTTに置き換えるなども今後検討していきたいと思います。