かばちんのエンジニアブログ

日々の経験の中で培った内容を備忘録も兼ねて記録していくブログです。少しでも誰かの役に立つために頑張って続けていけたらなと思います。

2022 最新版!「GAS + Twitter API v2」でリツイートBOTを作る

※TwitterAPIの有料化により無料で利用することが出来なくなりました。

GAS と Twitter API v2 を使用してサーバレスでリツイートBOTを作る方法

リツイート BOT とは?

リツイート BOT を作るために必要なもの

  1. Twitter アカウント
  2. Google アカウント

Twitterデヴェロッパーアカウントの作成

まずは Twitter Developer Portal にアクセスしてアカウントを作成します
作成方法は検索すると良質なサイトがたくさん見つかるのでなんとかなると思います
(既に作成済みの方は飛ばして頂いて問題ないです)

Twitter Developer Portal
developer.twitter.com


登録が完了したら BOT 用にアプリケーションを登録します。
こちらの登録方法も検索すると良質なサイトがたくさん見つかります。

この作業で必要なものは作成したアプリケーションについての以下の3点が取得出来ればOKです

  1. API Key」
  2. 「Secret Key」
  3. 「Bearer Token」


(参考サイト)
qiita.com
zenn.dev
auto-worker.com

作成したアプリケーションの設定

アプリケーションページの下の方に User authentication settings という欄があるので、設定ページを開きます。

User authentication settings
User authentication settings

使用するOAuth設定とアプリケーションタイプの設定は以下のように指定すれば問題ないです。

OAuthの設定を行います

アプリケーション権限は「Read and Write」に設定します。

アプリケーションに権限を与える

必須項目である「Callback URI」については後ほど説明しますのでひとまず何か入れておいてください。

Website URL」「Team of service」「Privacy policy」には Twitter アカウントの
プロフィールページ URL を入れておけばとりあえず大丈夫そうです。

Callback URIや必要な項目を埋める

以上で「Callback URI」以外の設定が完了しました。
続いて Google 側の設定を行なっていきましょう。

Googleスプレッドシートを作成

今回はサーバを利用せず Google スプレッドシートスクリプト機能である GAS (Google Apps Script) を
利用してリツイート BOT を作成するためまずはスプレッドシートを作成します。

今回はこんな感じで「検索ワード(ハッシュタグ)」、「いいね or RT」の種別と、
重複して投稿をしてしまわないように「最新TweetID」を保持するようにしてあります。

Google スプレッドシートを作成

GAS (Google Apps Script) を作成

メニューの「拡張機能」>「Apps Script」を選択して GAS (Google Apps Script) 編集画面を表示します。

Apps Script を起動

ライブラリの設定

GAS (Google Apps Script) を作成したらまず、利用するライブラリの設定を行います。
今回は最も利用されているであろうライブラリ「TwitterWebService」と「OAuth1」、
それからスプレッドシートを操作したいので「Sheet」サービスを登録します。

ライブラリの設定

「TwitterWebService」の登録方法

「ライブラリ +」の「+」マークを押して検索ウィンドウが表示されたら、スクリプトID に
1rgo8rXsxi1DxI_5Xgo_t3irTw1Y5cxl2mGSkbozKsSXf2E_KBBPC3xTF」を入力して検索します。
画像のように正常に検索できたら「追加」ボタンを押して登録します。
バージョンは「2」で問題ありません。

TwitterWebServiceの登録

「OAuth1」の登録方法も同様でスクリプトID は「1CXDCY5sqT9ph64fFwSzVtXnbjpSfWdRymafDrtIZ7Z_hwysTY7IIhi7s」です。
バージョンは「18」で問題ありません。

OAuth1を登録

Twitter の Callback URI を設定

先ほど飛ばしていた Twitter 側の Callback URI の設定に戻りましょう。
Callback URI は以下のような形式で入力する必要があります。

https://script.google.com/macros/d/{SCRIPT ID}/usercallback

{SCRIPT ID} の部分ですが、GAS (Google Apps Script) ページ URL
「〜/projects/XXXXXX/edit」の projects と edit に挟まれた部分になります。

https://script.google.com/macros/d/XXXXXX/usercallback    // ←こんな感じのイメージ(実際はもっと長い)

スクリプトを書く(全文掲載コピペOK)

ここまでの設定が出来たらあとはスクリプトを書けば完成です!
今回はスクリプト全文を載せておくので、必要箇所を書き換えてご自由にお使いください。

// ベアラートークンと認証用インスタンス
var bearerToken = 'ベアラートークン'
var twitter = TwitterWebService.getInstance(
  'Consumer API Key',         // 作成したアプリケーションのConsumer Key
  'Consumer API Secret Key'  // 作成したアプリケーションのConsumer Secret
);
var userID = '9999999999999999999'    // Twitter ユーザID

// 認証周り
function authorize() { twitter.authorize(); }                            // 認証
function reset() { twitter.reset(); }                                    // 認証解除
function authCallback(request) { return twitter.authCallback(request); } // 認証後のコールバック

// シートを取得
var sheetData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("シート1"); // 「シート1」はシート名

// APIコール時のオプション(GET)
const getOption = {
  method: 'get',
  contentType: 'application/json',
  muteHttpExceptions: true,
  headers: { Authorization: 'Bearer ' + bearerToken }
}
// APIコール時のオプション(POST)
const postOption = {
  method: 'post',
  contentType: 'application/json',
  muteHttpExceptions: true,
  headers: { Authorization: 'Bearer ' + bearerToken },
}

function main()
{
  var service = twitter.getService();

  var searchWords = pickUpSearchWords();
  for (var i = 0; i < searchWords.length; i++)
  {
    var searchWord = searchWords[i][0];
    var type = searchWords[i][1];
    var lastTweetId = searchWords[i][2];

    var tweetList = findTweets(service, searchWord, lastTweetId);
    if (tweetList == null)
    {
      continue
    }
    
    for (var j = 0; j < tweetList.length; j++) {
      var tweet = tweetList[j];
      if (tweet.id > lastTweetId) {
        lastTweetId = tweet.id;
      }
      if (type == 'いいね') {
        putFavorite (service, tweet);
      } else if (type == 'RT') {
        putRetweet (service, tweet);
      }
    }

    var titleRow = 1;
    var lastTweetIdCol = 3;
    var updateCell = sheetData.getRange(i + 1 + titleRow, lastTweetIdCol, 1, 1);
    updateCell.setValue(lastTweetId);
  }
}

// 検索ワードをスプレッドシートから取得する
function pickUpSearchWords()
{
  var titleRow = 1;
  var startRow = 1 + titleRow;
  var startCol = 1;
  var endRow = sheetData.getLastRow() - titleRow;
  var endCol = 3;
  
  return sheetData.getRange(startRow, startCol, endRow, endCol).getValues();
}

// ツイートを検索する
function findTweets(service, searchWord, lastTweetId)
{
  // API URL
  var getPoint = 'https://api.twitter.com/2/tweets/search/recent?query='

  // 検索キーワードとパラメータ
  var keyWord = encodeURIComponent(searchWord)
  var params = '&tweet.fields=author_id,id,text,created_at&max_results=20'
  params += '&since_id=' + lastTweetId

  // アクセスURL組み立て
  var url = getPoint + keyWord + params

  var response = service.fetch(url, getOption);
  var result = JSON.parse(response)
  return result.data
}

// いいね
function putFavorite(service, tweet)
{
  targetTweet = { 'tweet_id': tweet.id }
  postOption.payload = JSON.stringify(targetTweet)
  service.fetch('https://api.twitter.com/2/users/' + userID + '/likes', postOption);
}

// リツイート
function putRetweet(service, tweet)
{
  targetTweet = { 'tweet_id': tweet.id }
  postOption.payload = JSON.stringify(targetTweet)
  var result = service.fetch('https://api.twitter.com/2/users/' + userID + '/retweets', postOption);
}

スクリプトの細かい説明

変更する必要のある設定項目は以下の4つです。

  1. Bearer Token
  2. Consumer API Key
  3. Consumer API Secret Key
  4. Twitter ユーザID

「Bearer Token」「Consumer API Key」「Consumer API Secret Key」は、
Twitter 側で取得したものをそのまま貼り付けてもらえれば問題ありません。

Twitter ユーザID は設定ページを探したのですが見当たらなかったため、
Twitter ID」というWEBサービスを利用して抽出しました。
tweeterid.com

// ベアラートークンと認証用インスタンス
var bearerToken = 'ベアラートークン'
var twitter = TwitterWebService.getInstance(
  'Consumer API Key',         // 作成したアプリケーションのConsumer Key
  'Consumer API Secret Key'  // 作成したアプリケーションのConsumer Secret
);
var userID = '9999999999999999999'    // Twitter ユーザID


続いて、TwitterWebService に関連するメソッドを定義していきます。
ここは特に変更する必要はないため、決まり事として書いてあれば問題ありません。

// 認証周り
function authorize() { twitter.authorize(); }                            // 認証
function reset() { twitter.reset(); }                                    // 認証解除
function authCallback(request) { return twitter.authCallback(request); } // 認証後のコールバック


次に、スプレッドシートから検索ワードを抽出するためシートを操作できるようにします。
「シート1」というシート名で検索し、データを操作するための変数を作成します。
シート名をデフォルトから変更した場合はこちらも合わせて修正が必要になります。

// シートを取得
var sheetData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("シート1"); // 「シート1」はシート名


Twitter API v2 では、アクセスに Bearer Token が必要なため、TwitterWebService の
fetch メソッドで指定するオプションを定義しています。
GET と POST をそれぞれ分けましたが、method が get か post かという差しかないため 1 つに統一しても良いかもしれません。
ここで最初に設定した Bearer Token が使用されます。

// APIコール時のオプション(GET)
const getOption = {
  method: 'get',
  contentType: 'application/json',
  muteHttpExceptions: true,
  headers: { Authorization: 'Bearer ' + bearerToken }
}
// APIコール時のオプション(POST)
const postOption = {
  method: 'post',
  contentType: 'application/json',
  muteHttpExceptions: true,
  headers: { Authorization: 'Bearer ' + bearerToken },
}


いよいよ、スクリプトのメイン処理に入ります。
main メソッドでは以下のような流れで処理を行なっています。

  1. TwitterWebService の定義
  2. 検索ワードの取得
  3. 対象ツイートの検索
  4. ツイートが無ければ次の検索ワードへ
  5. ツイートがあれば「いいね」or「リツイート
  6. 最新 Tweet ID を更新

上記 2〜6 を検索ワードが無くなるまで繰り返します。

function main()
{
  var service = twitter.getService();

  var searchWords = pickUpSearchWords();
  for (var i = 0; i < searchWords.length; i++)
  {
    var searchWord = searchWords[i][0];
    var type = searchWords[i][1];
    var lastTweetId = searchWords[i][2];

    var tweetList = findTweets(service, searchWord, lastTweetId);
    if (tweetList == null)
    {
      continue
    }
    
    for (var j = 0; j < tweetList.length; j++) {
      var tweet = tweetList[j];
      if (tweet.id > lastTweetId) {
        lastTweetId = tweet.id;
      }
      if (type == 'いいね') {
        putFavorite (service, tweet);
      } else if (type == 'RT') {
        putRetweet (service, tweet);
      }
    }

    var titleRow = 1;
    var lastTweetIdCol = 3;
    var updateCell = sheetData.getRange(i + 1 + titleRow, lastTweetIdCol, 1, 1);
    updateCell.setValue(lastTweetId);
  }
}


実際にツイートを「検索」「いいね」「リツイート」しているのは以下の部分になります。

「検索」

Twitter API v2 のツイート検索エンドポイントは https://api.twitter.com/2/tweets/search/recent です。
GET でのアクセスになるため、query にそれぞれパラメータを付与してアクセスします。

パラメータには、検索ワード、取得フィールド(ユーザID、ツイートID、内容、日時)、最大取得件数の指定と、
重複防止のためにスプレッドシートに記録していた最新ツイートIDを使用して、
どのツイートIDのツイート以降を検索対象とするかを since_id で指定しています。

レスポンス形式は JSON です。

// ツイートを検索する
function findTweets(service, searchWord, lastTweetId)
{
  // API URL
  var getPoint = 'https://api.twitter.com/2/tweets/search/recent?query='

  // 検索キーワードとパラメータ
  var keyWord = encodeURIComponent(searchWord)
  var params = '&tweet.fields=author_id,id,text,created_at&max_results=20'
  params += '&since_id=' + lastTweetId

  // アクセスURL組み立て
  var url = getPoint + keyWord + params

  var response = service.fetch(url, getOption);
  var result = JSON.parse(response)
  return result.data
}


「いいね」「リツイート

それぞれのエンドポイントは以下の通り
いいね:https://api.twitter.com/2/users/{UserID}/favorites
リツイートhttps://api.twitter.com/2/users/{UserID}/retweets

UserID には WEBサービスTwitter ID」で取得したユーザIDを指定します。
オプションの payload に、どのツイートIDを対象にするかをセットします。

成功すると JSON で { "retweeted": true } こんな文字列(リツイートの場合)が返ってきます。

// いいね
function putFavorite(service, tweet)
{
  targetTweet = { 'tweet_id': tweet.id }
  postOption.payload = JSON.stringify(targetTweet)
  service.fetch('https://api.twitter.com/2/users/' + userID + '/favorites', postOption);
}

// リツイート
function putRetweet(service, tweet)
{
  targetTweet = { 'tweet_id': tweet.id }
  postOption.payload = JSON.stringify(targetTweet)
  var result = service.fetch('https://api.twitter.com/2/users/' + userID + '/retweets', postOption);
}

アプリ認証が必要

スクリプトを紹介する前に「スクリプトを書けば完成」と言いましたが実は最後にもう1つやることがあります。
Twitter に投稿させるには GAS (Google Apps Script) にアプリ認証をさせる必要があります。

アプリ認証方法

GAS (Google Apps Script) 画面の上部、「実行」「デバッグ」と書かれているところのすぐ右に、
プルダウンでメソッドを選択できるものがあると思います。
ここで「authorize」を選択して「実行」ボタンを押します。

すると、レスポンスに Twitter 認証用の URL が返ってくるのでブラウザでその URL にアクセスして認証を行います。
Callback URI の設定が正しく行われていれば正常に認証が完了するはずです。

実際に動かしてみる

初回は最新 Tweet ID がない状態なため、適当に何かツイートしてそのツイートIDを指定しておきます。
先ほどメソッド「authorize」を選択したところを「main」に変更して「実行」ボタンを押せば作動します。

検索ワードに設定されたキーワードに引っかかったツイートを自動的に
「いいね」又は「リツイート」してくれるようになったと思います。

定期的に実行させる

作成したいのは手動のツールではなく BOT なため、定期的に自動で実行するように設定します。
GAS (Google Apps Script) にはトリガーと呼ばれる自動実行用の仕組みが既に存在しているため、
そちらを利用して定期的に実行されるように設定を行います。

トリガー設定

GAS (Google Apps Script) 画面の左のメニューから「トリガー」画面を開きます。
画面右下の「トリガーを追加」ボタンを押して設定画面を開きましょう。

トリガーの詳細設定

今回それぞれの設定は以下のように設定を行いました。

実行する関数:main
実行するデプロイ:Head(最新)
イベントのソース:時間主導型
トリガータイプ:分ベースのタイマー
時間の感覚:1分おき

エラー通知設定:1週間おきに通知
(1分おきの実行でエラーが発生した場合頻繁に通知されることが想定されるため長めに設定)

リツイート BOT 完成

お疲れ様でした!
これで本当にリツイート BOT が完成しました!!

あとは Twitter アカウントを宣伝してフォローしてもらい、
実際にリツイート機能を Twitter ユーザに使ってもらえばいいだけですね。
(実はそこが一番大変なところ)

まとめ

今回リツイート BOT を作ろうと思ったきっかけは、BOT を求めているツイートをたまたま見かけて、
「そういえばリツイート BOT 作ったことないなぁ」なんて思ったのがきっかけです。

そこから実際に作りきるところまでやったわけですが、Twitter API v2 関連の情報が少ない印象でした。
やっぱり最終的に行き着くところは公式ドキュメントでした。公式最強!

2021年11月15日に Twitter API v2 が主要 API に変わったばかりということもあり、
TwitterWebService を利用して Twitter API v2 をコールする記事がとても少なかった気がします。

今回利用した API は「検索」と「いいね」「リツイート」のみですが、要領は同じだと思うので
他の API を利用するのにも少しは役に立つ内容になったのではないかなと思います。

おまけ

実際に作ったリツイート BOT はこちら
twitter.com