GASでTwitter検索&いいねを自動でやる

## はじめに

### 概要
○○について言及したツイートすべてにいいねをつけたいけど、手動では手間がかかるので自動でやってしまおう。
※[Twitterの利用規約](https://twitter.com/ja/tos)はきちんと守ろう。

### 使うもの
* [Twitter Developer Platform](https://developer.twitter.com/en)
* Google Apps Script

### 全体の仕組み
1. 任意の条件でツイートの検索をかける
1. ヒットしたツイートがあれば順番にいいねをつける（この中で一番新しいツイートのツイートIDを保存しておく）
1. 次回以降から検索をかけるときは前回保存したツイートIDより新しいツイートのみを対象にする

## つくりかた
#### 1. Twitter Developerでプロジェクトとアプリ作成・各種設定
[【0からわかる！？】Twitter API利用申請&基本操作コードまとめ【超入門】](https://norari-kurari-way.com/python_twitter_api_intro/) 
こちらの記事を参考に、Consumer Keys（API KeyとAPI Key Secret）を控えておくところまでやる。

#### 2. GASでOAuth1のライブラリをインストール
GASのプロジェクト内でスクリプトID `1CXDCY5sqT9ph64fFwSzVtXnbjpSfWdRymafDrtIZ7Z_hwysTY7IIhi7s` で検索をかけて、OAuth1のライブラリを追加する。

#### 3. GASでコード作成
```javascript:oauth.gs
// Consumer Keys
let apiKey = 'API Key';
let apiKeySecret = 'API Key　Secret';

// 登録するコールバックURLをログに記録
function logAuthorizeUri() {
  let twitterService = getTwitterService();
  console.log(twitterService.authorize());
}

// 指定の名称で新しいサービスを作成
function getTwitterService() {
  return OAuth1.createService('任意のサービス名称')
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authenticate')
      .setConsumerKey(apiKey)
      .setConsumerSecret(apiKeySecret)
      .setCallbackFunction('authCallback')
      .setPropertyStore(PropertiesService.getUserProperties());
}

// OAuth1の認証フローが完了したときのメッセージを表示
function authCallback(request) {
  let twitterService = getTwitterService();
  let isAuthorized = twitterService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}
```

```javascript:main.gs
// 自分のTwitter ID（※スクリーンネームではない）
let id = 'Twitter ID'

// ツイート検索といいね
function searchAndLikeTweets() {
  let twitterService = getTwitterService();
  if(twitterService.hasAccess()) {
    let targetTweetIds = searchTweets(twitterService);
    if(targetTweetIds !== null) {
      likeTweets(twitterService, targetTweetIds);
    } else {
      return;
    }
  } else {
    console.log(service.getLastError());
    return null;
  }
}

// ツイート検索
function searchTweets(twitterService) {
  let searchQuery = '任意の検索クエリ';
  let maxResults = '検索結果の上限件数';
  let sinceId = PropertiesService.getScriptProperties().getProperty('newestTweetId'); // 前回のツイート検索結果のうち一番新しいツイートのツイートIDをプロパティストアから取得
  let param = 'query=' + searchQuery + '&max_results=' + maxResults;
  // 初回実行時は以下をコメントアウト
  // let param = 'query=' + searchQuery + '&max_results=' + maxResults + '&since_id=' + sinceId;
  let url = 'https://api.twitter.com/2/tweets/search/recent' + '?' + param;
  let headers = {
    method: 'get',
    muteHttpExceptions: true
  }

  let response = twitterService.fetch(url, headers);
  console.log(response.getContentText());
  let result = JSON.parse(response.getContentText());
  let resultCount = result['meta']['result_count'];

  // ツイート検索結果が0件でなければ、各ツイートにいいね & 検索結果のうち一番新しいツイートのツイートIDをプロパティストアに保存
  if(resultCount !== 0) {
    let tweetIds = [];
    for(i = 0; i < result['data'].length; i++) {
      let tweetId = result['data'][i]['id'];
      tweetIds.push(tweetId);
    }
    let newestTweetId = result['meta']['newest_id'];
    PropertiesService.getScriptProperties().setProperty('newestTweetId', newestTweetId);
    return tweetIds;
  } else {
    return null; 
  }
}

// ツイートにいいね
function likeTweets(twitterService, targetTweetIds) {
  let url = 'https://api.twitter.com/2/users/' + id + '/likes';
  targetTweetIds.forEach(function(targetTweetId) {
    let payload = {
      'tweet_id': targetTweetId
    }
    let headers = {
      method: 'post',
      muteHttpExceptions: true,
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    }

    let response = twitterService.fetch(url, headers);
    console.log(targetTweetId + ': ' + response.getContentText());
  });
}
```

#### 4. OAuth認証をする
3で作成したlogAuthorizeUriを実行して、実行ログに吐き出されたURLにアクセスする。
Twitterログイン後、"Success! You can close this tab."と表示されれば認証成功。

#### 5. 初回だけ手動で実行する
さっそくメインの関数searchAndLikeTweetsを手動で実行して、実行ログを確認してみよう。

問題なければ、searchTweetsのコメントアウトされている行を元に戻して、その直前にある"let param = ..."の行は削除してしまおう。
そうすると次回からはツイート検索時に差分を取得するはず。

#### 6. GASでトリガー設定
さいごに、searchAndLikeTweetsのトリガーを好きなように設定すれば、定期的に実行してくれる。

## 参考ページ
* [GitHub - OAuth1 for Apps Script](https://github.com/googleworkspace/apps-script-oauth1)
* [【初心者向けGAS】プロパティストアの概要とスクリプトプロパティの入力方法](https://tonari-it.com/gas-property-store/#toc7)
* [idtwi](https://idtwi.com/) 👈Twitter IDが確認できるツール
* [API reference index | Twitter Developer Platform](https://developer.twitter.com/en/docs/api-reference-index)
* [【TwitterAPI v2】検索クエリ・オペレータ一覧](https://zenn.dev/mamushi/articles/twitter_search_query)