スプレッドシートで作ったtodoリストでステータスを変更するとslackに流す

[Google Apps Script Advent Calendar 2014](http://qiita.com/advent-calendar/2014/gas)の7日目の記事になります。よろしくお願いします。

[2日目](http://qiita.com/n0bisuke/items/ed99da94aba342af3cdc)の方のslackバージョンに近いかと思います。既出でしたら大変恐縮です、、。

画面小さいですが、通知される様子は、こんな感じになります。

![todoToSlack.gif](https://qiita-image-store.s3.amazonaws.com/0/46489/0f99798e-86fb-567e-b861-c74219cffaf7.gif)
※ ↑これgifなんですが、動いてない場合は[こちら](https://dl.dropboxusercontent.com/u/91774212/todoToSlack.gif)から...

slackに流れる内容は、例えばこんな感じです。
![Screen Shot 2014-12-06 at 16.48.15.png](https://qiita-image-store.s3.amazonaws.com/0/46489/ebdb0f1f-444a-b3f7-7218-27cbdf3ea451.png)


では、下記にとりあえず動くものを作る手順を書いて、その後に簡単に補足したいと思います。

# slack側の設定
## todo用のチャンネルを作成
 - slackの左側CHANNELSの下の方にある、Create a channel... からチャンネルを作成

## incoming hookの設定
 - https://yourslackdomainname.slack.com/services/new#diy から Incoming WebHooksのaddを選択
 - postしたいチャンネル(さっき作ったtodo)を選択して、Add Incoming WebHooks Integrationを選択
 - 下記のようなURLがでてくるのでこれをコピっておく

![Screen Shot 2014-12-06 at 16.06.34.png](https://qiita-image-store.s3.amazonaws.com/0/46489/3fee9146-e908-c9b3-7385-f9b8df586e11.png)

# google apps側の設定
## スプレッドシートの用意
 - 下記の感じで用意(列の範囲：B列~I列、todo開始行：3行目)

![Screen Shot 2014-12-06 at 16.08.28.png](https://qiita-image-store.s3.amazonaws.com/0/46489/e9a59ff5-ecd1-1511-284e-34561e9e3a22.png)

## gasの編集
 - ツール > スクリプトエディタを選択し新規に.gsファイルを作成
 - プロジェクト名、ファイル名を適当に入力
 - 下記のコードをコピペ
 - 1行目に、さっきslack側でコピっておいた、incoming hookのpost用urlをペースト

```gas:todo.gs
var postUrl = "https://hooks.slack.com/services/xxxxxxx/xxxxxxx/xxxxxx";
var postChannel = "#todo";

function myOnEdit()
{
  var keywordCol = 8;         // ステータス列idx
  var inChargePersonCol = 7;  // 担当者列idx
  var dataStartCol = 2;       // データが開始する列のidx
  var taskContentCol = 2;     // タスク内容列idx
  
  var colOffset = 1;  // A列はoffset
  
  var dataColNum = 8;  // データの列数
  var range = SpreadsheetApp.getActiveRange();
  var rowidx = range.getRow();
  var colidx = range.getColumn();
  if(colidx != keywordCol){ return ; }
  var sheet = SpreadsheetApp.getActiveSheet();
  
  // ステータスに変更のあったtodo1行
  var aLine = sheet.getRange(rowidx, dataStartCol, 1, dataColNum);
  if(!aLine){
    return;
  }
  
  // ステータスセルの値
  var status = aLine.getCell(1,keywordCol-colOffset).getValue();
    
  // 担当者セルの値
  var username = aLine.getCell(1, inChargePersonCol-colOffset).getValue();
  if(!username){
    username = "unknown";
  }
  
  // タスクの内容
  var taskContent = aLine.getCell(1, taskContentCol-colOffset).getValue();  
  if(!taskContent){
    return;
  }
  
  // ステータスを変更した人の名前(googleのアカウント名) 
  // todo: めんどくさそうなので一旦なしで
  // var editor = Session.getActiveUser().

  var message = username;
  message += "さんが、タスク「";
  message += taskContent;  
  message += "」のステータスを";
    
  if( status == '完了' ){
    aLine.setBackgroundColor("#efefef");
    aLine.setFontColor('black');
    message += "【完了】";
    
  }
  else if(status == '未着手' || !status){
    aLine.setBackgroundColor('white');
    message += "【未着手】";
  }
  else if(status == '進行中'){
    aLine.setBackgroundColor('#fce5cd');
    message += "【進行中】";
  }
  
  message += "にしました";
  
  sendHttpPost(message, "TODOテスト");
}

function test()
{
  sendHttpPost("上様が、タスク「ほげほげほげ」のステータスを【進行中】にしました", "TODOテスト");
}

function sendHttpPost(message, username)
{
  var jsonData =
  {
     "channel" : postChannel,
     "username" : username,
     "text" : message
  };
  var payload = JSON.stringify(jsonData);
  var options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  };
  
  UrlFetchApp.fetch(postUrl, options);
}
```

 - スクリプトを動かすために承認(下記参照)が必要なので、まずは`test()`を動かして、続行 > 承認 を選択

![Screen Shot 2014-12-06 at 15.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/46489/8325f1e4-dc27-3f21-7511-45e5ab703a22.png)

 - `test()`がうまく動いたら、gasのエディタメニューのリソース > 現在のプロジェクトのトリガーから、下記のように設定して保存~~(既にonEditが入っている場合は一旦トリガーを消して新たに追加)~~

![Screen Shot 2014-12-10 at 15.51.24.png](https://qiita-image-store.s3.amazonaws.com/0/46489/f0fddbdd-a561-1a18-888d-e1f4d7aa9f3e.png)


以上。これでうごくはず！


#解説と補足

## Simple Triggers
gasには、[Simple Triggers](https://developers.google.com/apps-script/guides/triggers/)というイベントハンドラがあります。今回使ったのは、onEditです。スプレッドシートの値を編集すると、呼ばれるます。なので、値を編集するごとに、slackに通知を飛ばす処理はここに入れたいわけです。

ですが、上記公式ドキュメントのRestrictionsの部分で
> They cannot access services that require authorization. For example, a simple trigger cannot send an email because the Gmail service requires authorization, but a simple trigger can translate a phrase with the Language service, which is anonymous.

とあるように、gmaiとか外部接続サービスとかは実行できないようになっています。なので、ちょっと[stackoverflowの方のwork around](http://stackoverflow.com/questions/20457847/urlfetch-in-an-event-failing)を参考に回避しています(できない場合はお知らせください)。

## 実行権限の承認
ga側の設定の途中で、`test()`を実行して、実行権限の承認を行っています。これをしないと恐らく動きません。自動的に承認する方法もあったかもしれませんが、これが手っ取り早かったので大抵これでやってます。

## slackに吐き出すテキストのカスタマイズ

```
  var jsonData =
  {
     "channel" : postChannel,
     "username" : username,
     "text" : message
  };
```
ここでやってます。ここでusernameとかchannnelを任意に変えれば吐かれる内容も変わります。また、もっとカスタマイズしたい場合は、https://yourslackdomain.slack.com/services のIncoming WebHooksから先ほど作成した、incoming hookの設定ができます。アイコンとか、デフォのusernameとか、デフォのチャンネルとか設定できます。また、上記のjsonDataの内容をカスタマイズしたい場合は、**Configured Integrations**とか、**Message Attachments**とかをみると参考になるかと思います。

![Screen Shot 2014-12-06 at 17.05.30.png](https://qiita-image-store.s3.amazonaws.com/0/46489/25607cbe-6fe4-3f69-748a-5cbd33f40ba0.png)


以上、簡単でしたが、[Google Apps Script Advent Calendar 2014](http://qiita.com/advent-calendar/2014/gas)の7日目の記事でした。

 - - - - - 
[訂正1]

Simple TriggersでonEdit()が呼ばれるように設定していますが、一定時間たつと自動的に外れてしまうようです。ですので、Simple Triggerで、myOnEdit()を呼ぶようにしました。また、myOnEdit()の引数と中身を少し編集しました。

[訂正2]
> 既にonEditが入っている場合は一旦トリガーを消して新たに追加

と書きましたが、そうすると他のプロジェクトのトリガーのonEditを消すことになってしまうので、、現在のプロジェクトのトリガーに新たに追加する形になります、大変失礼しました。

[訂正3]
> if(colidx != keywordCol){ return ; }

myOnEdit()に、上記1行を追加しました。これでステータス以外のセルを編集するごとに通知が飛んでいたのが修正されます。

変更の差分は、[revisions](http://qiita.com/mito_log/items/6457dc110b3478e3e530/revisions)をご確認ください。