【ChatGPTからコピペのみで作る】LINEボット入門 ライブコーディング（のリベンジ？記事）

この記事は下記イベントのリベンジ？記事です。
30分チャレンジの結果は・・

https://linedevelopercommunity.connpass.com/event/282146/


# 何をしようとしたのか？

ChatGPTにGASを使ったLINEbot（おうむ返し）の作り方を聞いて、ソースコードをそのままコピペするだけで動きました！がテーマ。ついでにスタンプとFlexMessageの応答も追加！結末は文末に貼った動画をご覧ください笑

それではリベンジ開始です。やり直してるので動画内のソースとは異なってます。

# シンプルに聞いてみる


```プロンプト.
GASを使ってLINEBotを作りたいんですが、どうしたらいいですか？
```


<details><summary>ChatGPTの応答（クリック）</summary>

```
LINE Messaging APIを使用してGAS（Google Apps Script）を使ってLINEBotを作成することができます。以下は、基本的な手順です。

1. LINE Developersにサインアップ: https://developers.line.biz/ja/
   LINE Developersに登録して、アカウントを作成します。

2. 新しいプロバイダーとチャネルの作成:
   LINE Developersコンソールにログインし、新しいプロバイダーとチャネルを作成します。LINEBotを作成するための必要な情報を提供します。

3. チャネルアクセストークンの取得:
   チャネルの設定ページからチャネルアクセストークンを取得します。このトークンは、GASからLINE Messaging APIにアクセスするために使用されます。

4. Google Apps Scriptプロジェクトの作成:
   Google Apps Scriptプロジェクトを作成します。これは、LINEBotのコードを実装する場所です。

5. GASにLINEBotのコードを実装:
   Google Apps Scriptプロジェクト内に、LINEBotのコードを実装します。具体的には、受信したメッセージに応じて適切な返答を生成し、LINE Messaging APIを使用してユーザーに応答を送信するコードを書きます。

6. LINE Messaging APIの設定:
   LINE Developersコンソールのチャネル設定ページに戻り、Webhook URLをGoogle Apps Scriptのエンドポイントに設定します。これにより、LINEBotがメッセージを受信し、GASのコードが実行されるようになります。

7. デプロイとテスト:
   Google Apps Scriptプロジェクトをデプロイし、LINEBotが正しく動作するかテストします。LINEアプリでLINEBotにメッセージを送信して、返答が正しく受信されるか確認しましょう。

以上が基本的な手順です。GASを使用してLINEBotを作成する場合、JavaScriptを使用してコードを実装する必要があります。また、LINE Messaging APIのドキュメントやサンプルコードを参考にすると、より具体的な実装方法を理解できるでしょう。
```
</details>

ちゃんとLINE Developersにサインアップしなさいと言ってますね。URLもついてます。
この通りにプロバイダとチャネルを作ってチャネルアクセストークンを取得します。
実際の操作などは動画も合わせてどうぞ。。

### まずはおうむ返しを実装

```プロンプト.
おうむ返しbotにしたいので、サンプルのソースを書いてください
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;

  // おうむ返しの処理
  var echoedMessage = userMessage;

  // LINE Messaging APIへの返信
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_CHANNEL_ACCESS_TOKEN'
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [{
      'type': 'text',
      'text': echoedMessage
    }]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}
```
</details>

なんか良さげです。

### アクセストークンを直接書かない

```
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_CHANNEL_ACCESS_TOKEN'
  };
```

この部分の　`YOUR_CHANNEL_ACCESS_TOKEN` の箇所です。ここには LINE Develoepersコンソールのアクセストークンを貼り付ける必要があります。

![スクリーンショット 2023-05-14 13.06.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/5592c7b1-bc96-c480-0cf8-c28f797b51c7.png)
が、コード内に直接記載したくないので、アクセストークンの部分はGASのスクリプトプロパティから取得します。

ということで再度聞いてみます。

```プロンプト.
アクセストークンを直接ソースに書きたくないので修正後のソースを書いてください
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
  var accessToken = getAccessToken();  // アクセストークンを環境変数から取得

  // おうむ返しの処理
  var echoedMessage = userMessage;

  // LINE Messaging APIへの返信
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [{
      'type': 'text',
      'text': echoedMessage
    }]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}

function getAccessToken() {
  // 環境変数からアクセストークンを取得
  var accessToken = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
  return accessToken;
}
```
</details>

ちゃんとプロパティから取得する実装に変わってます。
```
PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
```

GASの設定にてスクリプトプロパティに `CHANNEL_ACCESS_TOKEN` を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/53566b96-4733-f395-2bca-87b198761656.png)


### GASをデプロイし確認

無事におうむ返しされました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/e2ee2527-7ebe-ccb9-d85f-f2ebf15ab61e.png)

以降も、実装を更新の都度デプロイします。

### スタンプ応答を追加

続いて、「スタンプ」の文字を送ったときは特定のスタンプを返答するように機能を追加します。

```プロンプト.
「スタンプ」と送ったときは、特定のスタンプを返すように追加してください
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var messageType = JSON.parse(e.postData.contents).events[0].message.type;
  var accessToken = getAccessToken();  // アクセストークンを環境変数から取得

  // おうむ返しの処理
  var echoedMessage;
  if (messageType === 'text') {
    var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
    echoedMessage = userMessage;
  } else if (messageType === 'sticker') {
    var stickerMessage = getStickerMessage();  // スタンプメッセージを取得
    sendReplyMessage(replyToken, stickerMessage, accessToken);
    return;
  } else {
    echoedMessage = 'メッセージのタイプには対応していません。';
  }

  // LINE Messaging APIへの返信
  var textMessage = {
    'type': 'text',
    'text': echoedMessage
  };
  sendReplyMessage(replyToken, textMessage, accessToken);
}

function sendReplyMessage(replyToken, message, accessToken) {
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [message]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}

function getAccessToken() {
  // 環境変数からアクセストークンを取得
  var accessToken = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
  return accessToken;
}

function getStickerMessage() {
  var stickerMessage = {
    'type': 'sticker',
    'packageId': '1',
    'stickerId': '1'
  };
  return stickerMessage;
}
```

</details>

上記でも動きましたが、「スタンプ」という文字を送ったときではなく、スタンプ自体を送った時に特定のスタンプが返ってくる実装になってます。

```
} else if (messageType === 'sticker') {
```

何か適当なスタンプを送信すると、スタンプが応答されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/38088622-09d6-fc30-273a-85759624de07.png)

### 「スタンプ」のメッセージに応答するように修正

これでも良いのですが、「「スタンプ」というメッセージを受け取った時に」と修正してもらいます。


```
「スタンプ」というメッセージを受け取った時にスタンプが返ってくるようにしたいので、修正してください
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
  var messageType = JSON.parse(e.postData.contents).events[0].message.type;
  var accessToken = getAccessToken();  // アクセストークンを環境変数から取得

  // おうむ返しの処理
  var echoedMessage;
  if (messageType === 'text') {
    if (userMessage === 'スタンプ') {
      var stickerMessage = getStickerMessage();  // スタンプメッセージを取得
      sendReplyMessage(replyToken, stickerMessage, accessToken);
      return;
    } else {
      echoedMessage = userMessage;
    }
  } else {
    echoedMessage = 'メッセージのタイプには対応していません。';
  }

  // LINE Messaging APIへの返信
  var textMessage = {
    'type': 'text',
    'text': echoedMessage
  };
  sendReplyMessage(replyToken, textMessage, accessToken);
}

function sendReplyMessage(replyToken, message, accessToken) {
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [message]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}

function getAccessToken() {
  // 環境変数からアクセストークンを取得
  var accessToken = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
  return accessToken;
}

function getStickerMessage() {
  var stickerMessage = {
    'type': 'sticker',
    'packageId': '1',
    'stickerId': '1'
  };
  return stickerMessage;
}
```
</details>

「スタンプ」の文字を判定する実装に更新されてますね。

```
if (userMessage === 'スタンプ') {
```

「スタンプ」と送ったらスタンプが応答されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/4b9ac807-1081-0a33-6e43-b5dbd086a791.png)

### FlexMessageの応答を追加

ここまでは動画でも順調と言えばそこそこ順調でしたね・・。
最後にFlexMessageの応答を追加してもらいます。

```
「フレックスメッセージ」というメッセージを受け取った時に、特定のFlexMessageが返ってくるように機能を追加してください。
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
  var messageType = JSON.parse(e.postData.contents).events[0].message.type;
  var accessToken = getAccessToken();  // アクセストークンを環境変数から取得

  // おうむ返しの処理
  var echoedMessage;
  if (messageType === 'text') {
    if (userMessage === 'スタンプ') {
      var stickerMessage = getStickerMessage();  // スタンプメッセージを取得
      sendReplyMessage(replyToken, stickerMessage, accessToken);
      return;
    } else if (userMessage === 'フレックスメッセージ') {
      var flexMessage = getFlexMessage();  // FlexMessageを取得
      sendReplyMessage(replyToken, flexMessage, accessToken);
      return;
    } else {
      echoedMessage = userMessage;
    }
  } else {
    echoedMessage = 'メッセージのタイプには対応していません。';
  }

  // LINE Messaging APIへの返信
  var textMessage = {
    'type': 'text',
    'text': echoedMessage
  };
  sendReplyMessage(replyToken, textMessage, accessToken);
}

function sendReplyMessage(replyToken, message, accessToken) {
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [message]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}

function getAccessToken() {
  // 環境変数からアクセストークンを取得
  var accessToken = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
  return accessToken;
}

function getStickerMessage() {
  var stickerMessage = {
    'type': 'sticker',
    'packageId': '1',
    'stickerId': '1'
  };
  return stickerMessage;
}

function getFlexMessage() {
  var flexMessage = {
    'type': 'flex',
    'altText': 'Flex Message',
    'contents': {
      'type': 'bubble',
      'body': {
        'type': 'box',
        'layout': 'vertical',
        'contents': [
          {
            'type': 'text',
            'text': 'Flex Message',
            'weight': 'bold',
            'size': 'xl'
          },
          {
            'type': 'text',
            'text': 'This is a Flex Message.',
            'margin': 'lg'
          }
        ]
      }
    }
  };
  return flexMessage;
}
```
</details>

無事に機能追加されました。
```
 } else if (userMessage === 'フレックスメッセージ') {
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/aa8c684a-e62b-56da-ff81-199c8657a30b.png)

### FlexMessageに画像を追加

動画にもあるように指定の画像を返却してもらいたいと思います。

```プロンプト.
FlexMessageに画像を追加したいです。画像は以下を使ってください。
https://mashandroom.org/wp-content/uploads/2017/04/logo-320x320.png
```

<details><summary>ChatGPTの応答（クリック）</summary>

```
function doPost(e) {
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
  var messageType = JSON.parse(e.postData.contents).events[0].message.type;
  var accessToken = getAccessToken();  // アクセストークンを環境変数から取得

  // おうむ返しの処理
  var echoedMessage;
  if (messageType === 'text') {
    if (userMessage === 'スタンプ') {
      var stickerMessage = getStickerMessage();  // スタンプメッセージを取得
      sendReplyMessage(replyToken, stickerMessage, accessToken);
      return;
    } else if (userMessage === 'フレックスメッセージ') {
      var flexMessage = getFlexMessage();  // FlexMessageを取得
      sendReplyMessage(replyToken, flexMessage, accessToken);
      return;
    } else {
      echoedMessage = userMessage;
    }
  } else {
    echoedMessage = 'メッセージのタイプには対応していません。';
  }

  // LINE Messaging APIへの返信
  var textMessage = {
    'type': 'text',
    'text': echoedMessage
  };
  sendReplyMessage(replyToken, textMessage, accessToken);
}

function sendReplyMessage(replyToken, message, accessToken) {
  var url = 'https://api.line.me/v2/bot/message/reply';
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  };
  var payload = {
    'replyToken': replyToken,
    'messages': [message]
  };
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload)
  };
  UrlFetchApp.fetch(url, options);
}

function getAccessToken() {
  // 環境変数からアクセストークンを取得
  var accessToken = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
  return accessToken;
}

function getStickerMessage() {
  var stickerMessage = {
    'type': 'sticker',
    'packageId': '1',
    'stickerId': '1'
  };
  return stickerMessage;
}

function getFlexMessage() {
  var flexMessage = {
    'type': 'flex',
    'altText': 'Flex Message',
    'contents': {
      'type': 'bubble',
      'hero': {
        'type': 'image',
        'url': 'https://mashandroom.org/wp-content/uploads/2017/04/logo-320x320.png',
        'size': 'full',
        'aspectRatio': '1.91:1',
        'aspectMode': 'fit'
      },
      'body': {
        'type': 'box',
        'layout': 'vertical',
        'contents': [
          {
            'type': 'text',
            'text': 'Flex Message',
            'weight': 'bold',

```
</details>

ChatGPTの応答が途中で切れた場合は **続き** をお願いします。

```プロンプト.
続きを
```

<details><summary>ChatGPTの応答（クリック）</summary>


```
            'text': 'Flex Message',
            'weight': 'bold',
            'size': 'xl'
          },
          {
            'type': 'text',
            'text': 'This is a Flex Message.',
            'margin': 'lg'
          }
        ]
      }
    }
  };
  return flexMessage;
}
```
</details>

指定の画像を使ったFlexMessageが返ってきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119864/25d63ff8-4888-a15c-747c-634c21e0d687.png)

# 動画では・・・

Chrome拡張機能で音声入力を使ってわちゃわちゃと指示を出してましたが、ちゃんと手打ちするべきだったかなぁ・・
また30分リベンジしたいと思います。動画では AI と戦っている キノコ のやり取りをお楽しみください。

https://www.youtube.com/watch?v=YHPy_EZyazc

（参考）

https://qiita.com/h2m_kinoko/items/7723b050b203c7202a3e