Google Apps Script(GAS)で自己リツイートを行うTwitterbot

ついにTwitterAPIの有償化が発表されてしまいました……以下の内容を実行するとお金がかかるようになるでしょう。今後どうするかはTwitterからの詳しい発表を待って決めますが、今のところ日に3回の自動リツイートは終了する予定です……

Herokuの無料枠が終了するためHerokuで動かしていたTwitterbotを移転することになった。このbotは、他の人がTwitterで検索した時に検索欄が自分のツイートで埋まることがないように、過去にツイートしたものだったら自己リツイートによって再ツイートする仕様だった。スプレッドシートとGoogle Apps Scriptを使うことで無事移転に成功したので以下に示す。

---
# 空のGoogleスプレッドシートを作成
Twitterに投稿する記事を管理するため。
# スプレッドシートのツールタブから新しくGoogleフォームを作成
![IMG_8773.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/841fb1de-ada0-616f-4e6e-748f8e2888ac.jpeg)
スプレッドシートに追加するツイート内容を140文字に制限するため。制限の仕方は[この記事](https://www.ec-create.jp/google-forms/how-to-use/mojisuu-jougen-saitei-mojisuu/)を参照。
出来たらフォームから試しに記事を1つ投稿してみよう。A列がタイムスタンプ、B列が記事内容になるはず。シートの名前をArticlesに変更し、C列をツイートID欄に、D列をリツイートID欄に使用するため、C1、D1セルにそのように記入。

:::note info
定期的にリツイートしたい記事をすでにツイートしている場合、スプレッドシートの最終行のB列に記事内容、C列に当該ツイートのURLのID部分(例: https://twitter.com/shikai_shosetsu/status/1580038037131575296 の1580038037131575296の部分)を直接書き込んでください。既にリツイートしている場合はリツイートを取り消してください。
:::
# スプレッドシートの拡張機能タブからApps Scriptを選択
![IMG_8775.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/a237e4e3-a830-0d2d-70e8-c2aabeb0e769.jpeg)
エラーが出たらもう一回。

以下は[この記事](https://moripro.net/gas-twitter-bot/)と[この記事](https://moripro.net/gas-spreadsheet-random-tweet/)を主に参照した。
# ライブラリの導入
「TwitterWebService.gs」というライブラリを導入する。(ソースコードは[ここを参照](https://gist.github.com/M-Igashi/750ab08718687d11bff6322b8d6f5d90)(GitHubに遷移))
左にあるライブラリの＋マークをクリックする。
![IMG_8777.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/1b056648-b50e-8bb0-d538-fba3a68e0681.jpeg)
下記のプロジェクトキーをコピーして「ライブラリを追加」のスクリプトIDの欄に貼り付け、「検索」をクリックする。

**1rgo8rXsxi1DxI_5Xgo_t3irTw1Y5cxl2mGSkbozKsSXf2E_KBBPC3xTF**

バージョンが最新(2022年10月12日時点では2)かどうかを確認して「追加」をクリックする。
# Twitterアプリケーション登録
[こちらの記事](https://qiita.com/bakira/items/00743d10ec42993f85eb )を参考にしてConsumer Key、Consumer Secret、Access TokenとAccess Token Secretを入手する。コールバックURLは下記参照。
## コールバックURLの生成
コールバックURLにはGASプロジェクトのスクリプトIDが必要なため、確認する。
設定をクリック。![IMG_8779.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/71afa84f-0037-5049-6ff2-a0ea8ae98888.jpeg)
以下の位置にあるスクリプトIDを確認。
![IMG_8781.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/7253f7dc-7b98-ac43-6740-f915e3ce0af8.jpeg)
コールバックIDは下記の通り。

**https://script.google.com/macros/d/スクリプトID/usercallback**

スクリプトIDの欄を確認したIDに変更しよう。
# アプリの連携認証をする
GASのエディタに以下を入力。Comsumer KeyとConsumer Secretは先ほど取得した値に変更しよう。API Key、API Secret Keyって名前になってるかも。

```javascript
//認証用インスタンスの生成
var twitter = TwitterWebService.getInstance(
  'xxxxxxxxxx',//Consumer Key
  'xxxxxxxxxx'//Consumer Secret
);
 
//アプリを連携認証する
function authorize() {
  twitter.authorize();
}
 
//認証を解除する
function reset() {
  twitter.reset();
}
 
//認証後のコールバック
function authCallback(request) {
  return twitter.authCallback(request);
}
```
実行をクリック。「承認が必要です」と出るので「権限を確認」をクリック。「このアプリはGoogleで確認されていません」と出た場合は左下に灰色で小さく表示されている「詳細」をクリックし、「無題のプロジェクト（安全ではないページ）に移動」をクリック。次にプロジェクトのGoogleアカウントへのアクセス権限のリクエストを許可し、最後に実行する関数をauthorizeに設定して(①)再度実行ボタンを押そう(②)。![IMG_8784.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/58ca6fb8-0b93-325a-da4d-37cfa4288cd6.jpeg)
実行ログが出てくれば成功！実行ログに出てきたURLにアクセスし、ブラウザにID・パスワードを求められたら入力し、「連携アプリを認証」をクリック。ブラウザにSuccessと表示されれば、アプリの連携認証は完了！
# 本体の記述
先ほどエディタに入力した続きに以下のスクリプトをコピペしよう。userIDはbotにしたいアカウントのユーザーIDに変えてね。

```javascript
//本体
function post() {

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Articles');
  var leftTweet = []
  var lastRow = sheet.getLastRow();
  var tweetIdColumn = 3;
  var rtIdColumn = 4;
  var service  = twitter.getService();
  var endPointUrl = 'https://api.twitter.com/1.1/statuses/update.json';
  var timeLineUrl = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
  var userId = 'botにしたいアカウントのユーザーID';

  var tweetIdList = sheet.getRange(2, tweetIdColumn, lastRow-1, 1).getValues();
  var thereAreArticlesThatHasNotBeenTweeted = tweetIdList.some(function(value, index, array_b){
    return(value[0]==="")
  });
  if (thereAreArticlesThatHasNotBeenTweeted){
    Logger.log(tweetIdList);
    for (var i=0; i<=lastRow-2; i++) {
    if (tweetIdList[i][0]===""){
      leftTweet.push(i+2);
    }
  }}
  if (thereAreArticlesThatHasNotBeenTweeted){
    var article = sheet.getRange(leftTweet[0], 2).getValue();
    var response = service.fetch(endPointUrl, {
      method: 'post',
      payload: {
        status: article
      }
    });
    var json = service.fetch(timeLineUrl, {
      screen_name: userId,
      count: 1
    });
    var tw = JSON.parse(json);
    sheet.getRange(leftTweet[0], tweetIdColumn).setValue(tw[0]["id_str"]);
  }else{
    var row = Math.floor(Math.random() * (lastRow-1)) + 2;
    if (sheet.getRange(row, rtIdColumn).isBlank()===false){
      var retweetId = sheet.getRange(row, rtIdColumn).getValue();
      console.log("retweetID:" + retweetId);
      var unretweetUrl = "https://api.twitter.com/1.1/statuses/unretweet/" + retweetId + ".json";
      var response = service.fetch(unretweetUrl, {
        method: 'post'
      });
    }
    var tweetId = sheet.getRange(row, tweetIdColumn).getValue();
    console.log("tweetID:" + tweetId);
    var retweetUrl = "https://api.twitter.com/1.1/statuses/retweet/" + tweetId + ".json";
    var response = service.fetch(retweetUrl, {
      method: 'post'
    });
    var json = service.fetch(timeLineUrl, {
      screen_name: userId,
      count: 1
    });
    var tw = JSON.parse(json);
    sheet.getRange(row, rtIdColumn).setValue(tw[0]["id_str"]);
  };
}
```
# 指定の時間にスクリプトを自動実行する
時計のマークをクリックし、「トリガーの追加」をクリック。
![IMG_8786.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212736/82530c83-4440-faf8-58c1-eab06943c09e.jpeg)

**実行する関数はpostにしてください**。イベントのソースを時間主導型、日付ベースにし、動かしたい時刻を選ぶと毎日同じ時間にツイートかリツイートが実行される。一日に複数回動かしたい場合は、トリガーを何個も追加したほうが便利。

**これにて完成である！！！**
# 記事を削除するときの注意
記事を削除する場合は、スプレッドシートの該当する記事の行を削除してからツイートを消してください。スプレッドシートをそのままにするとエラーの原因になります。
# 記事を更新したいとき
記事の更新機能はありません。スプレッドシートの該当行を削除、ツイートを削除し、Googleフォームから新しく記事を投稿しなおしてください。
# 追記
404エラーが頻発する。3種類あって、１つはどうやらリツイートの取り消しに失敗しているようだ。一応デバック用にログを出力する行を追記した。エラ－メッセージが届いたときは左のメニューの「実行数」のタブを開き、リツイートの取り消しに失敗したツイートを探し、手動でリツイートの取り消しとリツイートIDの消去を行ってほしい。"No status found with that ID."とのことだから、IDが間違っているということなんだと思うんだけど、IDはTLから自動取得したものだし、なんで失敗しているのか正直お手上げ状態である。2つ目は"You have already retweeted this Tweet."というエラーで、このエラーはデバック文を足してからまだお目にかかってないのでまた来たらいろいろ確認しようと思う。3つ目は"Exception: Address unavailable"というもので、こちらもまた来たらおいおい確認しようと思う。