「ふぁぼった人に○○します！」の代替をGoogleAppsScriptで作った

# ことのはじまり
 * TwitterAPIには「いいね！」した人をリストする機能が無い。
 * favstarとかもあるけど鍵付きの人は認識できないっぽいし一部有料。
 * むしろ「いいね！」でなくていい。**欲しい人のTwitterアカウントが認識できればいい**。
 * 後で集計しやすい形が良い。.csvとか.xlsxとか.jsonとかとか…
 * Googleフォームなららくちん！　……でもTwitterID書いて投稿だったら簡単に偽装できる。
 * お、コレ行けるんじゃね？→[[GAS] Google Apps Script でTwitter複数アカウントを使い分ける](http://qiita.com/koba0124/items/82276dc0219ad015a7a1)
 * GoogleAppsScriptならGoogleスプレッドシートに書きだせる！


というわけで作成した時にハマリ所をいくつかピックアップ。
詳しい使い方とかはココで紹介しませんので、各自で*Qiitって*ください。
「ええい御託は良い、はやくコードを写さんか！」って人は[こちら](#実装)

# ハマり所とテクニック
## コールバック
```js
--- コード.gs ---
OAuth1.getCallbackUrl = function(scriptId) {
  return Utilities.formatString(
    'https://script.google.com/macros/s/%s/exec', scriptId);
}
～
// getServiceを上書き、スクリプトIDを手動設定に変更
twitter.getService = function() {
    ～
    .setScriptId(PropertiesService.getScriptProperties().getProperty("OPENED_SCRIPT_ID"))
～
function doGet(e) {
    if (e && e.parameter.oauth_verifier)
    ～


--- プロジェクトのプロパティ ---
OPENED_SCRIPT_ID        : ********************************************************
```

一番悩んだところ。
デフォルトのプロパティでは`https://script.google.com/macros/s/スクリプトID/usercallback`に飛ぶのだが、このページに飛べるのはGoogleの認証ができる人のみ。
既に公開している`https://script.google.com/macros/s/スクリプトID/exec`にはGoogleの認証無しで見れるので、こっちに飛ばしてパラメータでコールバック前か後かを判断する。

## 認証前のページ
```
--- Oath.html ---
<a target="_top" href=<?=auth_url?>Twitterで認証</a>
```
クリックで飛ばす仕組み。
というのも、**GAS上で表示するページは自動遷移ができない** らしい。
metaタグやJavaScriptは書いても無効化されてしまう。セキュリティの関係上であろうし、コレは仕方ない。
(*自動で別ページへ飛んで勝手にアヤシイサイトに入会させられて○○万円振り込め。　なんてよくある手口ですからね…*)

## スクリプトのプロパティ
```js
--- コード.gs ---
var twitter = TwitterWebService.getInstance(
  PropertiesService.getScriptProperties().getProperty("TWITTER_CONSUMER_KEY"),
  PropertiesService.getScriptProperties().getProperty("TWITTER_CONSUMER_SECRET")
);
```
コード内でグローバル変数的なもの、マジックナンバーのようなものを格納できるGASの機能。
`setProperty`もある。
QiitaやGitHubにコードを投稿した時にAWSのキーを消し忘れて大変なことに…　って事が無くなる効果もある。
これからも積極的に使っていきたくなる機能。(グローバル変数の乱立には注意)

良い設定方法の記事無かったので書きました ↓
[GoogleAppsScript　スクリプトのプロパティの超簡単な使い方](http://qiita.com/0Delta/items/7d8303eebbff4062069e)

## HTMLの作成
```html
--- コード.gs ---
var html_template = HtmlService.createTemplateFromFile('Comp');
html_template.name=user_info[3];
return html_template.evaluate();

--- Comp.html ---
  <body>
    <br /><?=name?>さんの受付が完了しました。</center>
  </body>
```
`HtmlService.createTemplateFromFile(拡張子なしファイル名)`で生成したテンプレートファイルに値を突っ込むだけでHTMLから参照できる。
最後にテンプレートを`evaluate()`して、出てきたオブジェクトをreturnで返してあげればページがユーザーに表示される仕組み。
プログラムでｺﾞﾆｮｺﾞﾆｮしてできた値を簡単にHTMLとして表示できる。

# 実装
 以下のライブラリを追加。
* TwitterWebService: MFE2ytR_vQqYfZ9VodecRE0qO0XQ_ydfb
* OAuth1: 1CXDCY5sqT9ph64fFwSzVtXnbjpSfWdRymafDrtIZ7Z_hwysTY7IIhi7s

#### コード.gs
```js
'use strict';

// OAuth1認証用インスタンス
var twitter = TwitterWebService.getInstance(
  PropertiesService.getScriptProperties().getProperty("TWITTER_CONSUMER_KEY"),
  PropertiesService.getScriptProperties().getProperty("TWITTER_CONSUMER_SECRET")
);

// コールバック先をexecに変更
// デフォルトのusercallbackだとGoogleでの認証が必要になってしまう。
OAuth1.getCallbackUrl = function(scriptId) {
  return Utilities.formatString(
    'https://script.google.com/macros/s/%s/exec', scriptId);
}

// getServiceを上書き、スクリプトIDを手動設定に変更
twitter.getService = function() {
  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/authorize')
    .setConsumerKey(twitter.consumer_key)
    .setConsumerSecret(twitter.consumer_secret)
    .setCallbackFunction('exec')
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScriptId(PropertiesService.getScriptProperties().getProperty("OPENED_SCRIPT_ID"))
}

// 認証時にURLを返すように変更
twitter.authorize = function() {
  var service = this.getService();
  if (service.hasAccess()) {
    Logger.log('Already authorized');
  } else {
    var authorizationUrl = service.authorize();
    return authorizationUrl;
  }
}

// 認証をリセット
function reset() {
  twitter.reset();
}

// 認証を行う
function authorize() {
  return twitter.authorize();
}

// スプレッドシートに追記
function addSpreadSheet(value){
  var id = PropertiesService.getScriptProperties().getProperty("SPREADSHEET_ID");
  var spreadSheet = SpreadsheetApp.openById(id);
  // 既に受付済の人は追記せずに帰る
  var idlst = spreadSheet.getSheetValues(2,3,spreadSheet.getSheetByName('data').getMaxRows(),1);
  for(var idv in idlst){
    if(idlst[idv][0]==value[2]){
      return;
    }
  }
  spreadSheet.getSheetByName('data').appendRow(value);
}

// ユーザ情報を取得
function getUserInfo() {
  var service  = twitter.getService();
  var response = service.fetch('https://api.twitter.com/1.1/account/verify_credentials.json');
  var response_list = JSON.parse(response)
  
  return [
    Date(),
    response_list["id_str"],
    '@'+response_list["screen_name"],
    response_list["name"],
  ]
}

function doGet(e) {
//test_code  
//    Comp.htmlをテストするときに此方を使う
//    var html_template = HtmlService.createTemplateFromFile('Comp');
//    html_template.name="テスト";
//    return html_template.evaluate();
//test_code
  // 初回、コールバック共に此方の関数を使う。
  // OAuthのパラメタ有無で認証前か後を判断する
  if (e && e.parameter.oauth_verifier){
    twitter.authCallback(e);
    var user_info = getUserInfo();
    addSpreadSheet(user_info);
    var html_template = HtmlService.createTemplateFromFile('Comp');
    html_template.name=user_info[3]; //name
    return html_template.evaluate();
  }else{
    reset();
    var html_template = HtmlService.createTemplateFromFile('Oath');
    html_template.auth_url=authorize();
    return html_template.evaluate();
  }
}
```

#### Oath.html
```html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <title>「ふぁぼった人に○○します！」の代替をGoogleAppsScriptで作った</title>
  </head>
  <body><center>
    <a target="_top" href=<?=auth_url?>Twitterで認証</a>
  </center></body>
</html>
```

#### Comp.html
```html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <title>認証完了</title>
  </head>
  <body>
    <br /><?=name?>さんの受付が完了しました。</center>
  </body>
</html>
```

#### プロジェクトのプロパティ -> スクリプトのプロパティ
```
TWITTER_CONSUMER_SECRET : **************************************************
TWITTER_CONSUMER_KEY    : *************************
SPREADSHEET_ID          : ********************************************
OPENED_SCRIPT_ID        : ********************************************************
```
* `TWITTER_CONSUMER_SECRET`と`TWITTER_CONSUMER_KEY` の二つはTwitterのアプリキー   
* `SPREADSHEET_ID`は書き込み先スプレッドシートID
* `OPENED_SCRIPT_ID`は、アプリを公開した時のID(一度Webに公開してキーをコピってから再公開)