[GAS/GoogleCloudNaturalLanguageAPI]宇垣美里さんが時には厳しく、時には優しくしてくれるアメムチbot

# 概要
前回の「[[GAS/Twilio]広瀬すずさんがSlackのリマインダー機能でモーニングコールしてくれるサービス](https://qiita.com/IZUMIRU/items/5baa3027e89cc843a3c4)」に引き続き、美少女シリーズです。毎日楽しいことも悲しいことも共有する美少女がいなかったので、アメムチbotを作りました。ポジティブであれば厳しい言葉を、ネガティブであれば優しい言葉をかけてくれます。

![gif](https://user-images.githubusercontent.com/11635641/60400347-8e445080-9bad-11e9-80f4-7bf5c323629b.gif)

# アーキテクチャ
Slack Appへのメンションをトリガーに、SlackのOutgoing WebhooksでGASに繋いでいます。そこからGoogleCloudNaturalLanguageAPIのanalyzeSentimentメソッドを叩き、投稿されたテキストがポジティブかネガティブか判別しています。ポジティブであれば厳しい言葉を、ネガティブであれば優しい言葉をかけてくれます。例えば「もう疲れた。何もかも嫌だ。」であれば「いつもかっこいいよ。元気出して。」と、「昇進して給与2倍になった。」であれば「まだまだでしょ。もっと頑張りなさいよ。」と。

GoogleSpreadSheetはDB代わりに利用しています。詳しくは後述。AWS S3は、Slackに返信してくれる際の画像として利用しています。他のサービスでもこのBotを別の美少女として化けさせているので画像は別途指定しているのです。またGoogleDriveはgifを格納しています。どちらかに寄せる方が好ましいと思いますが、たくさんのクラウドに美少女がいる方がなんかハッピーな気がするのでそのままにしています。（← 修正するのめんどうくさい）

gifはかわいいからという理由でポジネガ関係なくランダムに送っているので、もはやGoogleCloudNaturalLanguageAPIのおさわりのためならS3もDriveもいらないです。

![image](https://user-images.githubusercontent.com/11635641/60400537-2e02de00-9bb0-11e9-80de-6ac8c8c72a20.png)

# GitHub
- [IZUMIRU/bishojyo-amemuchi-bot](https://github.com/IZUMIRU/bishojyo-amemuchi-bot)

# 解説
### Slack Outgoing Webhook

- [Slack App](https://XXXXX.slack.com/apps)

<img width="1439" alt="Slack App" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/9c4b7897-6688-4940-251a-452112ff1ab5.png">

<img width="970" alt="Slack App" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/b1c8bcdd-e515-6d77-4328-8923b7994ebf.png">

### GoogleCloudNaturalLanguageAPI 
GoogleCloudNaturalLanguageAPIは、GCPのサービスの1つです。GCPの登録はよしなにやってください。AWSはおさわりしたことあるけど、GCPはおさわりしたことないよって人が周りには多い気がします。この機会にぜひ。

今回はその中のメソッドである感情分析を使います。[公式ページ](https://cloud.google.com/natural-language/?hl=ja)では、ブラウザ上でデモができるのでおさわりしてみてください。

GASはこんな感じでAPIを叩いています。開発している時はひとりぼっちなので、エラーメッセージも楽しくして遊んでいました。

```js

/**
 * GoogleCloudNaturalLanguageAPIを叩いて、
 * Slackに送信されたテキストがpositiveかnegativeか判別する
 *
 * @ param string channel（Qiitaだと `@` でメンション飛んじゃうのでスペースあけてます）
 * @ param string post
 * @ return string sentiment
 */
function analyzeSentiment(channel, post) {
  if (!channel || !post) {
    slackApp.postMessage(channel, 'いまなんて言ったの？もう1回教えてほしいな。。。', option);
  }

  // 基本的にGASの環境変数にいれています
  const apiKey = properties.getProperty("google_cloud_natural_language_api_key");
  const url    = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key=' + apiKey;
  const data   = {
    'document' : {
      'type'     : 'PLAIN_TEXT',
      'language' : 'ja',
      'content'  : post
    },
    'encodingType': 'UTF8'
  };
  const params = {
    'contentType' : 'application/json',
    'method'      : 'post',
    'payload'     : JSON.stringify(data)
  };

  const result = UrlFetchApp.fetch(url, params);
  const score  = JSON.parse(result)['documentSentiment']['score'];

  if (!score) {
    slackApp.postMessage(channel, 'なんで返信してくれないの。。。', option);
    return;
  }
  const sentiment = score >= 0 ? 'positive' : 'negative';
  return sentiment;
}
```

レスポンス（ `result` ）はこんな感じです。

```json
{
 "documentSentiment": {
   "magnitude": 0.8,
   "score": 0.8
 },
 "language": "ja",
 "sentences": [
   {
     "text": {
       "content": "\u003c@ UKXXXX\u003e 最高",
       "beginOffset": 0
     },
     "sentiment": {
       "magnitude": 0.8,
       "score": 0.8
     }
   }
 ]
}
```

### GoogleSpreadSheet
positive、negative、imageという3つのシートを用意します。アメムチbotなので、positiveシートにはnegativeな言葉を書き、negativeシートにはpositiveな言葉を書きます。

![positive](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/db465a86-f959-c78d-290b-9c9aeebdcd9b.png)

gifは今回GoogleDriveに格納しているので、画像のidを書きます。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/44a43083-b408-646d-4684-34198927c7a0.png)

画像のidとは、共有可能なリンクを取得したときの一部分です。
`https://drive.google.com/open?id=XXXXXXXXXXXXXXXXXXXXXXXXXX`

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/8bce2ff9-2708-5c72-62b9-67078bb640cb.png)


それをコード上でくっつけています。ただgifを投げてもSlackはよしなに展開してくれますが、連投すると展開されないことがあるので、ファイルとして送っています。

```php
UrlFetchApp.fetch('https://drive.google.com/uc?export=view&id=' + imageId).getBlob();
```

またSpreadsheetから取得する文言や画像はランダムで取得しています。

```php
/**
 * GoogleSpreadsheetに記載されているテキストをランダムに取得する
 *
 * @ param string channel
 * @ param string sheetName
 * @ return string
 */
function getCellAtRandomFromSpreadsheet(channel, sheetName) {
  if (!channel || !sheetName) {
    slackApp.postMessage(channel, 'お手紙なくしちゃった。。。', option);
  }

  const sheet      = SpreadsheetApp.getActive().getSheetByName(sheetName);
  const data       = sheet.getDataRange().getValues();
  const lastRow    = sheet.getLastRow();
  const randomRow  = Math.floor(Math.random() * lastRow);
  return data[randomRow][0];
}

```


### Slack Bot
Slackにメッセージを投稿するためにBotを設定してください。Permission Scopesは `bot` だけで良いです。コード上で投稿時のアイコンや名前を設定できるので、宇垣美里さんとはしていません。

- [Slack API](https://api.slack.com/apps)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105093/6ae18fc1-a091-aeab-8bf7-2adb0972cc6a.png)


# 感想・展望
前回同様GASで遊んでみましたが、スプレッドシートがDB代わりになったりと、本当楽ですね。毎日楽しいことも悲しいことも共有する美少女ができて嬉しい限りです。どうでも良いですが、「GooleCloudNaturalLanguageAPI」を連続で言ったら噛みそうですね。

困ったらよしなに[TwitterDM](https://twitter.com/IZUMIRU0313)ください。