Google Apps Scriptを使ってらくらくTypetalk BOT開発① Libraryの紹介


全国のbotファンの皆様こんにちは 大橋です。
Nulab社のディスカッションツール[Typetalk](https://typetalk.in/)は使っていますでしょうか？

なかなかシンプルな作りなのと、しっかりとしたAPIがあるので、素敵な限りです。
そして実はGoogle Apps Scriptは「Googleの情報を使って定期的にAPIを叩く基盤」としては最強に近くて、
今回のようなBot開発にはとても向いています。

今回はこのTypetalkのAPIを楽に扱うためのTypetalkAppというライブラリと、
その参考実装となる[Typetalk Google Calendar Bot](https://script.google.com/macros/s/AKfycby9vKIu5A44IIz6IzF1-fcOXYJ44K1x39TZ5vi3G-Z2WzLdzQg/exec)を作ったのでその話を書きたいと思います。
なお、結構ボリュームがあるので何回かに分けてかけたらなーと思ってます。
今回は一回目なのでLibraryの話をします。

なおGASSER向けに書くと、今回のLibraryとBotのアプリケーションは、
2014年2月に追加となった`ScriptApp.getOAuthToken()`と`StateTokenBuilder`を使ったOAuth2フローを体現しているアプリケーションなのでコードのサンプルになるかもしれません。

## Libraryをとりあえず使いたい( ｰ`дｰ´)ｷﾘｯ

### Libraryを取り込む

以下のライブラリをGASで取り込みます。
Library Key : `MQvyTczLCUS1XlKfdLRSaGgV62A3znfOO`

Libraryの取り込み方は[こちらの記事](http://qiita.com/soundTricker/items/7bbd86425ae8d0641d50)を参考にして下さい。

そうすると以下のように`TypetalkApp`というサービスが利用できるようになります。

![TypetalkCalendarBot.png](https://qiita-image-store.s3.amazonaws.com/0/2965/86516657-dd39-715f-1c5b-3b3a982a6e0a.png "TypetalkCalendarBot.png")

なおこのTypetalkAppは補完が効くように作成していますが、最初に`create`メソッドを呼び出し、インスタンスを取得して下さい。
![TypetalkCalendarBot_autocomplete.png](https://qiita-image-store.s3.amazonaws.com/0/2965/2e5d3dc2-7e64-19dd-bd80-83789f8aa751.png "TypetalkCalendarBot_autocomplete.png")



### Typetalk APIの認可をする

#### TypetalkにApplication登録する

まずAPIを利用するにはTypetalkにApplication登録をして、
ClientIdとClientSecretを取得する必要があります。

* Typetalkにて右上メニューから「アプリケーション設定」を選択
![Developer_Applications_-_Typetalk.png](https://qiita-image-store.s3.amazonaws.com/0/2965/26112069-227e-af15-95fb-a4e371e3906b.png "Developer_Applications_-_Typetalk.png")

* 左メニューから「デベロッパー」を選択
![Typetalk_developer.png](https://qiita-image-store.s3.amazonaws.com/0/2965/9795a671-f79b-9138-c04e-1f85851bddca.png "Typetalk_developer.png")

* 右上の新規アプリケーションを選択

![Typetalk_Developer_Applications_-_Typetalk.png](https://qiita-image-store.s3.amazonaws.com/0/2965/d997f838-9cef-e707-4515-57708cdeb3a5.png "Typetalk_Developer_Applications_-_Typetalk.png")

そうすると以下の画面が出ます。
![Typetalk_AddApplication.png](https://qiita-image-store.s3.amazonaws.com/0/2965/f18748bd-af4d-6cad-b052-081c832e967d.png "Typetalk_AddApplication.png")


GrantTypeは`Client Credentials`と`Authorizatoin Code`の二つがありますが

* Typetalk上の自分のアカウントの情報を取得したり、自分のアカウントでポストしたい
    * Client Credentialsを選択
* 自分以外のアカウントの情報を取得したり、自分以外のアカウントでポストしたい
    * Authorization Codeを選択

という考えになります。

#### GrantTypeでClient Credentialsを選択した場合

特に設定は必要ありません。
アプリケーション名や、icon画像などは自由に設定して問題無いと思います。

#### GrantTypeでAuthorization Codeを選択した場合

「リダイレクト URI」以外の項目は認可済みアプリケーション画面や、認可画面に表示される項目となります。

![Typetalk_setting.png](https://qiita-image-store.s3.amazonaws.com/0/2965/84423bc5-38c0-cf05-baa7-3c34a66276b5.png "Typetalk_setting.png")

という感じで設定していた場合は、

![Typetalk_Banners_and_Alerts_と_Authorized_Applications.png](https://qiita-image-store.s3.amazonaws.com/0/2965/b3043546-3522-8264-27ad-a291880dacf7.png "Typetalk_Banners_and_Alerts_と_Authorized_Applications.png")

という感じで表示されます。

##### 超重要「リダイレクト URI」の設定

これにミスると全くうまく行きません。
ここにはGoogle Apps Scriptのコールバック専用URLを設定する必要があります。

コールバックURLを作るにはいくつか手順が必要です。

1. まず[2013年度版 5分で始めるWebアプリケーション(Google Apps Script)](http://qiita.com/soundTricker/items/a4878d7e3100082576e4)を参考にLibraryを使う予定のGASプロジェクトでWebアプリケーションとして動作するように設定します。
2. 以下のテストコードを1.で作成したプロジェクトに作成し、このテストコードを走らせます。

```js:showCallbackURL.js
funciton showCallbackURL() {
  Logger.log(TypetalkApp.create("dummy","dummy",[]).getCallbackURL("dummy"));
}
```
 
3. ログコンソールを表示(Ctrl + Enter)して、`https://script.google.com/.../usercallback?state=...`を取得し`?state=`までをコピーします。

4. コピーしたURLを「リダイレクト URI」に設定します。

これで設定は完了です。


### Libraryを使ってGASからTypetalkの認可を行い、APIのAccessTokenを取得する。

TypetalkAppを利用して、Typetalk APIを呼び出すにはAccessTokenを取得する必要があります。

上記でGruntTypeに何を選んだかによって少しコードが異なります。


#### Client Credentialsを利用した場合

GruntTypeで`Client Credentials`を設定している場合は、
特にWeb画面を作らなくてもコードだけでAccessTokenを取得できます。

それを行うには以下のようにします。


```js:authorize.js

function authorize() {

  var clientId = "上記で取得したClientID";
  var clientSecret = "上記で取得したClientSecret";
  var scopes = [
    TypetalkApp.SCOPE_MY,        //自身のプロフィールを取得する場合のSCOPE
    TypetalkApp.SCOPE_TOPIC_POST,//投稿する場合のSCOPE
    TypetalkApp.SCOPE_TOPIC_READ //投稿を取得する場合のSCOPE

  ]; //scopeは配列で指定します。TypetalkApp.SCOPE_で補完可能です。
  
  var typetalkApp = TypetalkApp.create(clientId, clientSecret, scopes);

  if(!typetalkApp.isAuthorized()) { 
    //typetalkApp.isAuthorized()はtypetalkAppにて認可済み(AccessTokenを取得済み)かを判定します。
    //typetalkAppは内部でaccessTokenなどの情報をUserPropertiesに保存するため、一度呼び出していれば、以降は特に呼び出す必要はないです。
    //これらの認証情報を削除したい場合はtypetalkApp.deleteCredential()メソッドを呼び出して下さい。

    typetalkApp.authorize();
  }

}

```

#### Authorization Codeを利用した場合

Webアプリケーションとして、認可フローが必要になります。

色々コードは必要なのですが、ざっくり言うと

1. Typetalkの認可用URLを取得して画面に表示する関数
2. Typetalkにて認可後、GASのWebアプリケーションに戻ってきた際に処理する関数

の二つが必要になります。

以下がそのサンプルです。

```js:oauthFlow.js

//Typetalkの認可用URLを取得して画面に表示する関数
function doGet(e) {
  //TypetalkApp.create()を呼び出す処理をラップしています。
  var typetalkApp = getTypetalkApp(); 
  // var typetalkApp = TypetalkApp

  if(typetalkApp.isAuthorized()) {
    return showLoggedin(); //AccessToken取得済みの場合はログイン後の画面を表示
  }
  
  //"callback"は下に書くcallback関数のことを指しています。
  var authorizeUrl = typetalkApp.getAuthorizeUrl("callback");

  //画面にリンクを表示する
  return HtmlService.createHtmlOutput('<a href="' + authorizeUrl + '" target="_blank">Typetalkで認可する</a>');

}

//Typetalkにて認可後、GASのWebアプリケーションに戻ってきた際に処理する関数
//Typetalkで認可が完了すると、この関数が呼ばれます。
function callback(e) {

  var typetalkApp = getTypetalkApp();
  // var typetalkApp = TypetalkApp
  
  //typetalkAppのsaveAccessTokenに以下のようにcodeパラメータを渡すとLibrary側でAccessTokenを取得し、保存してくれます。
  typetalkApp.saveAccessToken(e.parameter.code);
  
  //完了後画面を表示する(HtmlService)とその画面がユーザに表示されます。
  return showLoggedin();
}

```

これで上記GASの画面を表示すると認可を行うためのリンクが表示され、Typetalkへ遷移し、戻ってきてAccessTokenを取得できます。

## Libraryを使ってTypetalkにポストしてみる

上記で認可が完了していれば準備は完了です。
まずは投稿してみましょう。

```js:postMessage.js
function postMessage() {
  var typetalkApp = getTypetalkApp();
  // var typetalkApp = TypetalkApp

  if(!typetalkApp.isAuthorized()) { 
    typetalkApp.authorize();
  }

  //topicを取得
  var result = typetalkApp.getTopics();

  //topicIdを取得 topicIdはtypetalkの画面に行って、トピックを表示した際にURLに表示されているものでもOK
  var topicId = result.topics[0].topic.id;

  //対象トピックにポスト
  typetalkApp.postMessage(topicId, "ほげほげ");
  

}

```

# まとめ

上記の認証周り以外の処理はだいたい補完できるので、
補完を見て頂いて叩いていただければ分かるかなーと思います。

次回ぐらいにもっと細かいApps Scriptとの組み合わせとかのことを書く予定です。

なおそういうのが待てない方はサンプルとしてGoogle Calendar Botを作ったので見てみて下さい。

## Calendar Botのコード
https://script.google.com/d/1B5aH-aFmAVrgS8QGDTw070C0FcfYxLFGylDYeyGqRwmjuIVIhtFzSN6L/edit?usp=sharing


# 追記

次の話を書きました。
http://qiita.com/soundTricker/items/6b633314e04331fcf3ed