英語学習システムをSlackとGASで作る

## はじめに
仕事で海外とのやり取りが必須であり，必死で英語を勉強中．
Inputを増やしているが，日々のインプットを復習する機会がほしいと感じていた．
そこで，SlackとGoogleAppsScript(GAS)を利用して，英語学習システムを構築してみた．

具体的には，個人用Slacワークスペースに英語学習用チャネルを作成して，
OutgoingWebhookを利用して以下機能を実装した．

- SlackからGoogle翻訳（和訳、英訳）
- Slackから学習した英文を記録
- Slackへ学習内容の復習を通知


## 実装機能
### SlackからGoogle翻訳（和訳、英訳）
Google翻訳でぱぱっと意味を確かめたい/英訳したいな,と思う機会が多かったので，SlackからGoogle翻訳が使えるようにしてみた．

「en 英語文」と入力すると和訳させる，「ja 日本語文」と入力すると英訳されるようにした．

### Slackから学習した英文を記録
「learn 英語文:意訳やメモ」と入力すると，スプレッドシートに
タイムスタンプ，英語文，意訳やメモ，英語文のGoogle翻訳による和訳結果
を記録する．

### Slackへ学習内容の復習を通知
上記で記録しているデータを，復習としてSlackへ通知する．
やり方は，タイムスタンプで判別して抽出する．
ここでは3つの復習パターンでSlack通知するようにした．

|No.| 投稿内容| 投稿間隔 |
|----|----|----|
|1|昨日の記録内容|毎日|
|2|昨日〜1週間前の記録内容|毎週土曜日|
|3|1週間前〜2週間前の記録内容|毎週日曜日|

## GAS


### doPost(e)
下記のSlack投稿に対応して動作する．

|トリガーワード | 入力 | 動作 |
|----|----|----|
|ja |日本語 |英訳|
|en |英語 |和訳|
| learn |学習内容:メモ| スプレッドシートに保存|

```js
function doPost(e) {
  
  // token確認
  var verify_token = LogGasApp.token_learning;
  if (verify_token != e.parameter.token || e.parameter.user_name=="slackbot") {
    throw new Error("invalid token.");
  }

  // check trigger_word
  var trword = e.parameter.trigger_word;
  var text = e.parameter.text;
  if(trword == "learn"){
    // learning
    text = text.substr(6); // remove trigger_word from text
    postLearning(text);
    var message = "What you learned was posted";
  }else if(trword == "ja"){
    //ja
    text = text.substr(3); // remove trigger_word from text
    var translated = LanguageApp.translate(text,"ja","en");
    var message = "en: "+translated;
  }else if(trword == "en"){
    //en
    text = text.substr(3); // remove trigger_word from text
    var translated = LanguageApp.translate(text,"en","ja");
    var message = "ja: "+translated;
  }
  
  // message
  var payload = {
    "text": message
  }
  var option = {
    "method": "POST", //POST送信
    "contentType": "application/json",
    "payload": JSON.stringify(payload) //POSTデータ
  }
  UrlFetchApp.fetch(LogGasApp.url_learning, option);
}
```

### postLearning(test)
Slackにて[learn 学習内容:メモ]と投稿すると，スプレッドシートに保存する．

```js
//=======================================================
// 学習内容を投稿
//=======================================================
function postLearning(test) {
  var sheet = SpreadsheetApp.openById(LogGasApp.ss_english).getSheetByName("fromSlack");
  var num = sheet.getLastRow();
  var text=test.split(":");
  var translated = LanguageApp.translate(text[0], "en", "ja");
  
  sheet.getRange(num+1,1).setValue(new Date());
  sheet.getRange(num+1,2).setValue(text[0]);
  sheet.getRange(num+1,3).setValue(text[1]);
  sheet.getRange(num+1,4).setValue(translated);
}
```
動作イメージ(en, ja)
<img width="367" alt="スクリーンショット 2017-11-15 23.32.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/199902/bcc7605e-3b13-e3aa-0c0c-5dc03478da97.png">

動作イメージ(learn)
<img width="559" alt="スクリーンショット 2017-11-15 23.30.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/199902/134f4a69-9bf9-ba41-522c-32827d9b8366.png">



### review
reviewYesterday(), review7days(), reviewLastWeek() の3種類をそれぞれタイムトリガーをセットして実行させる．

```js
//============ review: 英語の復習（昨日） ============
function reviewYesterday() {
  return postReview(1,0,"");
}
//============ review: 英語の復習（1週間） ============
function review7days() {
  return postReview(7,0,"");
}
//============ review: 英語の復習（2週間前〜1週間前） ============
function reviewLastWeek() {
  var message="You learned these in the last week. Let's review! \n\n";
  return postReview(14,8,message);
}


//=======================================================
// postReview: 復習を投稿する
//=======================================================
function postReview(start,end,message) {
  var sheet = ss_english.getSheetByName("フォームの回答 1");
  var num = sheet.getLastRow();
  var data = sheet.getRange(2, 1, num-1,2).getValues();
  var sheet2 = ss_english.getSheetByName("fromSlack");
  var num2 = sheet2.getLastRow();
  var data2 = sheet2.getRange(2, 1, num2-1,4).getValues();
  var dash = "----------------------------------"
  
  if (message=="") {
    var message= "You learned these in the last "
               + (start-end) + " days. Let's review! \n\n";
  }
  
  message = dateCheck(message,data,num,start,end);
  message = dateCheck(message,data2,num2,start,end);

  return postMessage("translation",message);
  
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  function dateCheck(message,data,num,start,end){
    var today = new Date();
    var start = new Date(today.getFullYear(), today.getMonth(),today.getDate()-start);
    //Logger.log("start:"+start);
    start=start.getTime();
    var end = new Date(today.getFullYear(), today.getMonth(),today.getDate()-end);
    //Logger.log("end:"+end);
    end=end.getTime();
    for (var i=0; i<num-1; i++) {
      var date = data[i][0];
      var time = date.getTime();
      
      if (time > start && end > time) {      
        message = message + "\n" + dash + dash ;
        var text = data[i][1];
        var memo = data[i][2];
        var translated = LanguageApp.translate(text, "en","ja");
        var message = message + "\n\n" 
                    + formatedDate(date) + ": "
                    + "<"+text + "> : " + translated
                    + " "
                    + "{" + memo + "}";
      }
      var date_check = date.getDate();
    }
    
    Logger.log(message);
    return message;
  }
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
```
動作イメージ
<img width="685" alt="スクリーンショット 2017-11-15 23.30.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/199902/c18ee238-3e5f-3f0c-d72f-0729036a1b10.png">


## 最後に
タイムトリガーをリマインダや復習目的に使うのは便利かもしれない
復習内容についても単純に時期で判断するだけではなく，クイズ形式にしたり，正解・不正解を記録しても面白いかも