【GAS×LineBot】応援してくれる子を作った

# はじめに
生きていく上で、つらいこと、嫌なこと、たくさんありますよね？
筆者は理系大学生なので、毎日研究つらいよ～～～と泣いています。

そこで、日々の愚痴を聞いて、励ましてくれるLineBotを作りました。
その名も、「**応援してくれる女の子**」です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202861/c8ab8079-c9e0-186c-e263-44b99c4560a8.png)

以下から友達追加できます👇
<a href="https://lin.ee/ubBjG35"><img src="https://scdn.line-apps.com/n/line_add_friends/btn/ja.png" alt="友だち追加" height="36" border="0"></a>


この記事のスタンスですが、「Google Apps Script(通称: GAS) でこういうことが出来たよ」と筆者の経験を共有することに重点を置いています。
そのため、コードの詳しい解説や、Line Messaging API の利用方法やGASのデプロイ方法の部分は最小限とさせていただきます🙇 

初心者の方がこの記事を読んで自分の手元で再現するのはちょっと難しいかもしれませんが、GAS で出来ることのイメージはつくと思います。

- [Linebotを作ろう](https://qiita.com/GeekSalon/items/280b649e92604eeda5dc)
- [Messaging APIとGASを使ったLINE Botでグループチャットの活発化](https://qiita.com/MxShun/items/7a563a795d41cdc0f1dc) の「初心者に向けて」の部分

上記の素晴らしい先駆者様は、画像を使いながらセットアップの説明しているので、プログラミング初心者の方でも手を動かして再現しやすいのではないかな？と思います。おすすめです。


# 使ったもの

- Google Apps Script(通称：GAS)
- LINE Messaging API

※2021年11月作成

# 手順

## ①Lineアイコンを作る

まず初めに、LineBotのアイコンを作ります。
自分を応援してくれる存在を思い浮かべながら、それにふさわしい写真や画像にしましょう！

## ②スクリプトを書く

Google Driveから「スプレッドシート」を新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/d41a3590-e67f-8dbc-7101-8d6396b96844.png)

名前を適当に決め（今回はCheergirlappとしました。）、「拡張機能」から「**Apps Script**」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/1135206e-76b0-5c05-de54-4580d8eb6310.png)


以下のコードを貼り付けます

```javascript
function doPost(e) {

  const event = JSON.parse(e.postData.contents).events[0]; //lineから送られてきた情報を取得しています。

  const replyToken = event.replyToken;

  if(typeof replyToken === 'undefined') return; // もしreplyTokenが空だったときのエラー処理　
  if(event.type !== 'message') return;

  const userMessage = event.message.text; //event.message.textにはユーザーが送ってきたメッセージ内容が入っています。

// ここから返すセリフを決めるコード
  const serifu = ["頑張ってるね、偉いね♡", "うんうん。大変だったよね。お疲れ様。","そうだよね。分かるよ。私も同じだから、その気持ちすっごく分かる。"] 
  let replyMessage = serifu[Math.floor(Math.random()*serifu.length)]

// ここから特定の言葉が含まれていたときに返すセリフを決めるコード
  if(userMessage.match("つらい")){
      replyMessage = "つらいよね。そうだよね……。そうだ、今日はもう休もうよ。明日はいい日になるかも！"
  }
  if(userMessage.match("ありがとう")){
      replyMessage = "ありがとうって……。私は何にもしてないよ。でも、嬉しい。こちらこそありがとう！"
  }
  if(userMessage.match("好き")){
      replyMessage = "今、私のこと好きって言った？えへへ、ありがとう、嬉しいな。"
  }

// ここから実際に返事をするコード
  const CHANNEL_ACCESS_TOKEN = "取得したアクセストークン";
  const url = 'https://api.line.me/v2/bot/message/reply';

  // 以下は返事を送信するための記述です
  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': [{
        'type': 'text',
        'text': replyMessage,
      }],
    }),
  });

}
```

セリフの部分は、自分の好きなように変えることもできます。
このコードだと、以下のような仕様になっています。

- メッセージが来ると、「頑張ってるね、偉いね♡」, 「うんうん。大変だったよね。お疲れ様。」,「そうだよね。分かるよ。私も同じだから、その気持ちすっごく分かる」の3つの言葉からランダムに選択して、返事を行う。
- `メッセージに「つらい」が含まれている場合`、「つらいよね。そうだよね……。そうだ、今日はもう休もうよ。明日はいい日になるかも！」
- `メッセージに「ありがとう」が含まれている場合`、「ありがとうって……。私は何にもしてないよ。でも、嬉しい。こちらこそありがとう！」
- `メッセージに「好き」が含まれている場合`、「今、私のこと好きって言った？えへへ、ありがとう、嬉しいな。」
- 上記3つのメッセージの優先度は好き > ありがとう > つらい。
    - 例えば、「つらいけど今の仕事は好きです」と言うと、「今、私のこと好きって言った？えへへ、ありがとう、嬉しいな。」と返事する。勘違いしちゃう系Botちゃんも可愛い。

## ③Line Messaging APIを使って、新しくLineBotのアカウントを作る

### アカウントを作る

[LINE Messaging APIのトップページ](https://developers.line.biz/ja/services/messaging-api/)の「今すぐはじめよう」をクリックします。ログインを求められるので、LINEアカウントでログインしてください。

### LINEBOTの基本設定を行う
ログインが出来たら、基本設定を行います。
アプリ名は決定したら7日間変更できないので、注意してください。
webhook通信を「利用する」に変更します。URLは後で設定するので、今はスルーして大丈夫です。

### アクセストークンを取得する
Messaging API タブの一番下に`Channel access token`という枠があります。
アクセストークンをコピーして、上記のコードの`"取得したアクセストークン"`の記述に上書きします。

```JavaScript

// ... 省略 ...

  // こんな感じのアクセストークンになるはず！
  const CHANNEL_ACCESS_TOKEN = "VC/IrlYHA/iakfvubeanraorn/J/7iSIJUyi1zuOSE0uqGc4TmIhFTSA4gaeviyvYW96qu4fgu3RmIolScu58uyQdlldQhgfrdrjkrauaerknhufvkjfriojarionfaevaragheioauh4DIlowVruIgdB04t89/1O/w1cDnyilFU=";
  const url = 'https://api.line.me/v2/bot/message/reply';

// ... 省略 ...
```


## ④デプロイする
Apps Scriptのアクセストークンを書き換えられたら、**ウェブアプリケーション**としてデプロイしましょう！

## ⑤Webhookを設定する
デプロイを行うとウェブアプリのURLが得られます。こんな感じのやつです。
`https://script.google.com/macros/s/AKfycbzyBrqBYjjtrtjhQOFCpKgOcpbQ73rHWXgjKDNAVsWqfqKlhF3g4DmKsarevncpxma/exec`

これを、Messaging API のWebhookに登録します。
これで、完成です！

※デプロイする度にURLが変わるので、機能修正を行うなどで再びデプロイをした際は、再度Webhookを設定し直してください。

# まとめ

ここまでやったことをまとめます。

- [x] Line Messaging API から、LineBot を設定する
- [x] GAS を書く

これで、いつでもどこでも愚痴を聞いて応援してくれる子を作ることが出来ました。

# 発展

ここまでで完成した Linebot ちゃんですが、残念ながらできることはそんなに多くありません。

ここからは、発展編として機能を追加することで、もっと応援してもらおうと思います。

## 発展① AI に返事をしてもらう

現在の Linebot ちゃんの最大の問題点は、「返事のレパートリーが少ない」ということです。
ユーザーからのメッセージに「好き」「ありがと」「つらい」という**決まり文句**が含まれている場合はちゃんと返すことができますが、決まり文句が含まれていない場合、話が一切通じない可能性があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/21d6f649-fda0-6314-42a5-6d4e9d6009b9.png)

まだ終わってないのに、無限に「うんうん。大変だったよね。お疲れ様。」と言われ続けています。まだ終わってないのに……。

そこで、AI との併用です。具体的には、以下のような仕様で実装します。

- 決まり文句が含まれていた場合：従来通り決まった回答を行う
- 決まり文句が含まれていない場合：AI に回答してもらう

AI によって回答させることで、意図しない言葉であっても、ある程度の会話が可能となります。
一方で、全て AI によって回答させないことで、本来の目的である「応援してくれる子」を実現できるという寸法です。

AI を作るのは難しいので、今回は誰かが作って一般公開してくれた AI の API を利用することにします。

利用できる API は様々なものがあります。

- [Talk API](https://a3rt.recruit.co.jp/product/talkAPI/)
- [Chaplus β版](http://www.chaplus.jp/api)
- [かたらい](https://www.katar.ai/)：30日間のフリープラン有

今回は、Chaplus を利用して AI 部分を実装することにします。

### メールアドレスを登録して、APIキーを取得する
[Chaplus](http://www.chaplus.jp/api) の公式サイトに、メールアドレスを登録すると、 API キーが送られてきます。

### コードを書き換える

Apps Script を開いて、コードを書き換えます。（コードの書き方は開発者の方が書いた「[雑談に応答するLINE Botを爆速で作成する方法(Google Apps Script + Chaplus雑談対話API)](https://qiita.com/maKunugi/items/e6fc7f51071ab3696d5f)」を参考にしました。）

```JavaScript

var LINEBOT_TOKEN = "LineBotのチャンネルアクセストークン"
var CHAPLUS_TOKEN = "ChaplusのAPIキー"


function doPost(e) {
  var event = JSON.parse(e.postData.contents).events[0]
  var replyToken = event.replyToken

  if (typeof replyToken === "undefined") {
    return
  }

  var userId = event.source.userId
  var username = getUserName(userId)

  if (event.type == "message") {
    var userMessage = event.message.text
    var replyMessage = ""

    // スプシにログを取る機能。なくてもOK。
    appendRow(userMessage,username)

    // もし決まり文句が含まれていたら、それに対応する言葉を返事して終わり。
    if(userMessage.match("つらい")){
      replyMessage = "つらいよね。そうだよね……。そうだ、今日はもう休もうよ。明日はいい日になるかも！"
    }
    if(userMessage.match("ありがとう")){
      replyMessage = "ありがとうって……。私は何にもしてないよ。でも、嬉しい。こちらこそありがとう！"
    }
    if(userMessage.match("好き")){
      replyMessage = "今、私のこと好きって言った？えへへ、ありがとう、嬉しいな。"
    }
    if(replyMessage !== ""){
      sendMessage(replyToken, replyMessage)
      return;
    }

    // 決まり文句が含まれていなかったら、Chaplueに答えてもらう
    replyMessage = getChaplusMessage(userMessage, username)
    sendMessage(replyToken, replyMessage)
  }
}

function sendMessage(replyToken, replyMessage) {
  var url = "https://api.line.me/v2/bot/message/reply"
  UrlFetchApp.fetch(url, {
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
      Authorization: "Bearer " + LINEBOT_TOKEN,
    },
    method: "post",
    payload: JSON.stringify({
      replyToken: replyToken,
      messages: [
        {
          type: "text",
          text: replyMessage,
        },
      ],
    }),
  })
}

function getUserName(userId) {
  var url = "https://api.line.me/v2/bot/profile/" + userId
  var userProfile = UrlFetchApp.fetch(url, {
    headers: {
      Authorization: "Bearer " + LINEBOT_TOKEN,
    },
  })
  return JSON.parse(userProfile).displayName
}

function getChaplusMessage(mes, username) {
  // 完全一致の場合、以下の返答をする
  var utterancePairs = [
    {
      utterance: "おはよう！", // この文が送られた場合、
      response: "今日も1日頑張って！", // この文を返す
      options: "お布団から出られない,仕事行きたくない", // と同時に、これらを次の発言候補としてユーザーに提案する。
    },
  ]
  var dialogue_options = {
    utterance: mes,
    username: username,
    agentState: {
      agentName: "LINE Botのチャネル名", 
      age: "0歳", //任意で数字を変更
      tone: "normal", //normal(標準)、kansai(関西弁)、koshu(甲州弁)、dechu(赤ちゃん言葉)の中から設定
    },
    addition: {
      utterancePairs: utterancePairs,
    },
  }
  var options = {
    method: "POST",
    contentType: "text/json",
    payload: JSON.stringify(dialogue_options),
  }

  var chaplusUrl = "https://www.chaplus.jp/v1/chat?apikey=" + CHAPLUS_TOKEN
  var response = UrlFetchApp.fetch(chaplusUrl, options)
  var content = JSON.parse(response.getContentText())

  var answer = content.bestResponse.utterance
  return answer
}

// スプシにログを取る機能
function appendRow(text,username) {
  var spreadsheet = SpreadsheetApp.openById("スプレッドシートのID")
  var sheet = spreadsheet.getSheetByName("シート名")
  sheet.appendRow([new Date(), text, username])
  return text
}

```

上記のコードをApps Scriptに上書きコピペして、デプロイをし直しましょう！
応援してくれる子が完成しました！✨
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/f5846cba-8a83-36db-f0a8-162f6d8c25d3.png)


これまでより、かなりリアルじゃないですか？

## 発展② スプレッドシートから決まり文句を持ってくる

今までの方法だと、決まり文句を増やすたびにif文を増やし、デプロイし、WebhookのURLを更新する必要があります。でも、冷静に考えて面倒じゃないですか？

決まり文句をスプレッドシート (以下:スプシと表記) に書いて、それをApps Scriptに読み取ってもらえれば、毎回コードを書き換える必要がなくなり、デプロイし直す必要がなくなります。
何かと便利なので、やってみましょう。

### 決まり文句をスプシに書き出す

スプシに「セリフ集」というシートを作り、A列に決まり文句、B列にレスポンスを書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/0a0a74d4-25fb-ac9f-efcf-d4b16bcde03b.png)


### コードを書き換える
変更前の該当コード

```javascript:before
// もし決まり文句が含まれていたら、それに対応する言葉を返事して終わり。
    if(userMessage.match("つらい")){
      replyMessage = "つらいよね。そうだよね……。そうだ、今日はもう休もうよ。明日はいい日になるかも！"
    }
    if(userMessage.match("ありがとう")){
      replyMessage = "ありがとうって……。私は何にもしてないよ。でも、嬉しい。こちらこそありがとう！"
    }
    if(userMessage.match("好き")){
      replyMessage = "今、私のこと好きって言った？えへへ、ありがとう、嬉しいな。"
    }
    if(replyMessage){
      sendMessage(replyToken, replyMessage)
      return
    }

```

変更後

```javascript:after
// もし決まり文句が含まれていたら、それに対応する言葉を返事して終わり。
    var spreadsheet = SpreadsheetApp.openById(
      "スプシのID"
    )
    var sheet = spreadsheet.getSheetByName("セリフ集")
    const lastRaw = sheet.getLastRow()
    const dataRange = sheet.getRange(2, 1, lastRaw - 1, 2)
    const data = dataRange.getValues()

    for (let i = 0; i < lastRaw-1; i++) {
      if (userMessage.match(data[i][0])) {
        replyMessage = data[i][1]
        sendMessage(replyToken, replyMessage)
        return;
      }
    }
```

これで終わりです！例としてスプシの6行目に「おさかな」「おいしいよね、おさかな」と入力してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/7d9500b0-517b-8c28-e46c-4614d849d6cf.png)

LineBotで「おさかな」と入力してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610894/450bb40c-d8ef-d658-fdbd-e03ea4df0c3e.png)

1つ目の「おさかな」がスプシを書き換える前、2つ目が書き換えた後です。
スクリプトは変えていないので、デプロイをし直す必要はありません。

これで、簡単に自分の好きなようにカスタマイズすることが出来るようになりました。

# 終わりに(宣伝)
ここまで読んでいただき、ありがとうございました！
この子の応援を元に卒論頑張ります👨

また、この記事は大学生限定クリエイティブコミュニティGeekSalon Advent Calendar 2021の企画の一部として投稿させていただきました！

GeekSalonは大学生限定のプログラミングスクールです。私もメンターとして所属しています。

もしご興味のある方がいれば、下のリンクからご覧になってください😊

https://geek-salon.com/