[GAS] GoogleAppsScriptでTwitterbotを作る

# 動機

毎日自動でTwitterの名前を変えたかった。

# 要件

- ある1ユーザーのTwitterの名前を毎日0時に自動で変更したい

# 前提知識

- TwitterAPIは**OAuth**認証を用いる
- OAuthは現在主に用いられているバージョンとして**1.0a**と**2.0**の2つがある
  - 単なるバージョン違いという訳ではなく、**認証の仕組みから違う**ため、別物である（互換性がない）
- TwitterAPIではOAuth2.0での認証は一部のAPIしか対応していないため、1.0aを使う
  - TwitterではOAuth2.0を使う認証を**Application-only authentication**、1.0aを使う認証を**Application-user authentication**と呼んでいる
  - [Oauth with the Twitter API — Twitter Developers](https://developer.twitter.com/en/docs/basics/authentication/overview/oauth)

# TwitterAPIの利用準備

TwitterAPIを用いるには開発者申請が必要。
[Twitter API 登録 \(アカウント申請方法\) から承認されるまでの手順まとめ　※2018年9月時点の情報 \- Qiita](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)

# GASでTwitterAPIを使う方法

公式でGASでOAuth1.0a認証をやるためのライブラリがあるのでそれを使う。
https://github.com/gsuitedevs/apps-script-oauth1

ちなみに**OAuth2.0用のライブラリも存在する**が、そもそもの認証の仕組みが違うので、**1.0aには使えない**ため注意。

## 1. ライブラリを導入する

README.mdに書かれている通りに進める。
GASのメニューから「リソース」->「ライブラリ」を選択し、以下のプロジェクトキーを入力し「追加」を押す。
`1CXDCY5sqT9ph64fFwSzVtXnbjpSfWdRymafDrtIZ7Z_hwysTY7IIhi7s`

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/6a540c3e-e9f6-14a4-d059-211dc2af8c7c.png" width="400">

バージョンはとりあえず最新のものを選んでおけば良さそう。

## 2. TwitterのAppにリダイレクトURLを設定する

リダイレクトURLは`https://script.google.com/macros/d/{SCRIPT ID}/usercallback`という形式になる。
スクリプトIDはGASのメニューから「ファイル」->「プロジェクトのプロパティ」で確認できる。
<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/6a93fd47-4b5d-b170-72bd-5d6ed4836946.png" width="400">

TwitterのAppの設定を開き、CallbackURLに設定する。
<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/01a107b5-f74c-4d8b-eaa6-14f8d247fb5c.png" width="400">


## 3. 対象ユーザーのアクセストークンを取得する

まず以下のようなスクリプトを用意する。

```js
// Twitter AppのConsumer Api Key
var CONSUMER_KEY = "*****";
var CONSUMER_SECRET = "************";

// 認証URLを取得しログに出力する
function logAuthorizeUri() {
  var twitterService = getTwitterService();
  Logger.log(twitterService.authorize());
}

// OAuth認証をよしなにしてくれるサービスクラスのインスタンスを生成・取得する
function getTwitterService() {
  return OAuth1.createService('Twitter')
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authenticate')
      .setConsumerKey(CONSUMER_KEY)
      .setConsumerSecret(CONSUMER_SECRET)
      // リダイレクト時に実行されるコールバック関数を指定する
      .setCallbackFunction('authCallback')
      // アクセストークンを保存するPropertyStoreを指定する
      .setPropertyStore(PropertiesService.getUserProperties());
}

// リダイレクト時に実行されるコールバック関数
function authCallback(request) {
  var twitterService = getTwitterService();
  // ここで認証成功時にアクセストークンがPropertyStoreに保存される
  var isAuthorized = twitterService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}
```

CONSUMER_KEY,CONSUMER_SECRETはTwitterAppの設定画面から確認できる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/97014/73ecd147-f208-adec-3f05-25f451d7a9f4.png)

### 認証手順

logAuthorizeUriを実行し、認証URLを取得する。

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/912cdf1c-3075-2cf8-3a76-6055c318ad1f.png" width="400">

ログにURLが出力されているので、メニューの「表示」->「ログ」からログを表示し、URLをコピーしてアドレスバーに貼って直接アクセスする。
（普通のTwitterアプリだと自動で移動するが、GASのstandalone scriptだとそれができないためこういった形になる）

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/ff1e429f-3c46-3600-31fc-e6a5875a4a2d.png" width="400">

アクセスするとTwitterの認証画面が出るので、認証ボタンを押す。

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/459161b6-ee7d-e992-60cd-828d48747166.png" width="600">

リダイレクトされて以下のような画面が出れば成功。

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/ebc801d0-ccd4-31d0-6aa6-ab0e588f1ad7.png" width="600">

一度認証を行うと、Twitter側で認証を解除しない限り同じアクセストークンが使える。
**アクセストークンはPropertyStore上に保持されている**ため、認証が解除されない限りこの手順をもう一度行う必要は無い。

参考：[GASで永続化する方法まとめ（設定や処理結果を保存・読み込みしたい時） \- Qiita](https://qiita.com/tomboyboy/items/6a15ab1a4838bd6b7f9d)

## 4. TwitterAPIを叩く

以降はOAuthのライブラリがよしなにしてくれるので、難しいことを気にせずともAPIを叩くことができる。
例えば名前を変更したいなら以下のような感じ。**makeRequest**と**changeName**が肝。

API：[POST account/update\_profile — Twitter Developers](https://developer.twitter.com/en/docs/accounts-and-users/manage-account-settings/api-reference/post-account-update_profile)

```js
// 名前を今日の日付に変更する
function changeTodayName()
{
  var todayName = getTodayName();
  changeName(todayName);
}

// 今日の名前を取得する
function getTodayName()
{
  // TODO: 日によってちゃんと変わるように実装する
  return "テスト";
}

// Twitterに名前変更のリクエストを送る
// name: 変更したい名前
function changeName(name)
{
  makeRequest("account/update_profile.json", {"name": name});
}

// TwitterにAPIリクエストを送る
function makeRequest(api_url, parameters) {
  var twitterService = getTwitterService();
  if (twitterService.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/' + api_url;
    var response = twitterService.fetch(url, {
      method: "post",
      muteHttpExceptions: true,
      payload: parameters
    });
    var result = JSON.parse(response.getContentText());
    Logger.log(JSON.stringify(result, null, 2));
  } else {
    Logger.log(service.getLastError());
  }
}
```

changeTodayNameを実行すると名前が「テスト」に変わることが確認できる。

## 5. 毎日0時にトリガーを設定する

毎日0時に名前を変更するために、自動で実行するための設定を行う。

メニューの「編集」->「現在のプロジェクトのトリガー」から設定画面に移行し、右下の「トリガーを追加」ボタンを押す。

<img src="https://qiita-image-store.s3.amazonaws.com/0/97014/f0d24feb-4dd9-5ae3-359b-4431adab5827.png" width="500">

よしなに設定を行い、「保存」で終了。

日付ベースの設定だと分単位で指定できないので、厳密に0時0分に変更したい場合には分ベースで1分単位で実行し、スクリプト側で時間判定を行ってAPIを叩くのが良さそう。

# まとめ

- TwitterAPIはOAuth1.0aが標準であり、2.0では一部APIしか使うことができない（2019/03現在）
- GASでOAuth認証を行うためのライブラリが公式で用意されている
- GASにはPropertiesServiceというデータ永続化のための便利クラスがあり、そこにアクセストークンを保持することで認証状態を保持できる

OAuth1.0aと2.0で情報が錯綜しているのと、GASでTwitterAPIを使うスクリプトは各所にあるものの、動作原理が解説されておらずいまいち理解できなかった（特にアクセストークンがどこに保持されるのかわからなかった）ので、少し苦労しました。
スプレッドシートと連携したりすればもっと色々できると思います。

## 参考資料

- [Google Apps Script で Twitter を使ってみる \- Qiita](https://qiita.com/Ikanogo/items/1dce33c26559eac56a03)
- [TwitterAPIを使ってOAuth2\.0のフロー（Client Credentials）を確認した。 \- takapiのブログ](http://takapi86.hatenablog.com/entry/2017/08/17/145940)