5分でできるLINE Bot + AI画像生成

# はじめに
この記事は[5分でできるLINE Bot + AI応答](https://qiita.com/cog1t0/items/cc7779345a01192d8f01)をベースに、DALL-Eという生成AIに画像を作ってもらえるようにした記事です
プログラミング初心者でも5分で画像生成LINEBotが作れることを目指して手順を極力簡単にしました。
LINEBot自作のとっかかりとして活用してください！

# 前提
- Googleアカウントを持っていること
- [LINE Developers](https://developers.line.biz/ja/)にログインできること
- [LINE Messaging API](https://developers.line.biz/ja/docs/messaging-api/overview/) についてある程度理解しておくと進めやすいです
- [openAI](https://openai.com/)でアカウントを作成していること

# 手順
## 目次
1. LINE Developersにログインし、設定を行い、トークンを発行します
1. LINEBotのテンプレートをコピーし自分のGoogleフォルダに移します
1. GoogleAppScriptのコードにトークンを書き込んで、デプロイをします
1. デプロイすると発行されるURLをLINE Developersで設定します
1. オウム返しBotが完成！
1. openAIにログインし、API Keyを発行
1. GoogleAppScriptにAPI Keyを書き込み、AIの設定を行う

という手順で、5分でできます！

## LINE Developersにログインし、設定を行い、トークンを発行します
[LINE Developers](https://developers.line.biz/ja/)にLINEのアカウントでログインします

![スクリーンショット 2022-05-15 0.33.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/2a0f327d-9d4a-2487-41e6-71010f240588.png)

適当な名前でプロバイダーを作成します
![スクリーンショット 2022-05-15 0.33.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/8452e972-1325-c43a-dbc9-2cd4d60897cd.png)

![スクリーンショット 2022-05-15 0.34.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/a60fc6ad-0728-930a-3937-399ce7c8ed4d.png)

MessagingAPIでチャンネルを作成します
![スクリーンショット 2022-05-15 0.34.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/c04425e2-d525-c1bd-6201-c043fffa7b0b.png)


チャンネルの必須項目を適当に埋めて作成します
![スクリーンショット 2022-05-15 0.35.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/f1c060d3-27f5-d5ca-8255-ba7b98c2e2cf.png)

![スクリーンショット 2022-05-15 0.35.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/4f651955-b322-3f39-0d98-9c193323520a.png)
![スクリーンショット 2022-05-15 0.36.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/d3f76200-8d0c-194a-82c8-5df33497a55a.png)

MessagingAPI設定から設定を行います
![スクリーンショット 2022-05-15 0.43.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/bbee39d4-8ed9-8849-3f3c-531e7ad8b49a.png)

設定するのは2つです
・応答メッセージをオフにする
・チャンネルアクセストークンを発行し、あとで使うのでコピーしておく
![スクリーンショット 2022-05-15 0.43.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/28f536a2-2537-1412-bf3e-0ff578e94896.png)

## LINEBotのテンプレートをコピーし自分のGoogleフォルダに移します
LINEBotのテンプレートを用意しているので 
https://docs.google.com/spreadsheets/d/1fCp7YzMUH17geUutyaLW_kqOQS0r_fzCvqShQT_S77s/edit
を開いて、自分のフォルダにコピーします

![スクリーンショット 2024-01-01 3.29.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/250aa60c-059d-7513-4b96-8d26d6514b01.png)

![スクリーンショット 2024-01-01 3.29.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/a90ea2dd-da20-892f-4b32-7868d211e24f.png)

## GoogleAppScriptのコードにトークンを書き込んで、デプロイをします

:::note
これ以降は、コピーした自分のスプレッドシートで作業します
コピー元のスプレッドシートはもう利用しないので閉じてください
:::

スプレッドシートを開き、拡張機能 > Apps Scriptを開きます
![スクリーンショット 2022-05-14 19.57.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/80158186-b2bb-a101-c89e-bce54344f0e7.png)

さきほどの手順でコピーしたチャネルアクセストークンで、3行目の「**LINE　Developersから取得したチャネルアクセストークン（長期）で書き換えます**」を上書きし、保存します
![スクリーンショット 2024-01-01 3.31.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/a8fbdee1-7c16-2d4a-6513-96c76f0edb4d.png)


画面右上の「デプロイ」から「新しいデプロイ」を選択します
![スクリーンショット 2024-01-01 3.32.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/941b4e19-1934-bba6-3bbd-e9547053a831.png)



左の歯車のアイコンを押し、「ウェブアプリ」をクリックします
![スクリーンショット 2022-05-14 19.59.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/234fbc3d-678e-9d7a-4425-120322c00dff.png)

以下のように設定して、デプロイを押します
「説明文」 => 適当に入力
「次のユーザーとして実行」 => 自分
「アクセスできるユーザー」 => 全員
![スクリーンショット 2022-05-14 19.59.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/5e891f07-b682-78c7-544b-ed9ee89dec59.png)


アクセスを承認をクリック
![スクリーンショット 2022-05-14 20.00.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/b7cf4a54-0eb3-6578-dd23-09960fb4e3ae.png)

アカウントを選択し
![スクリーンショット 2024-01-01 3.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/dc42d0c8-20f2-7ed9-e38a-a78a5c5a1fa1.png)



Advanced(詳細)をクリックし、
ページへ移動
![スクリーンショット 2024-01-01 3.49.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/514a0a9b-d48e-5e96-70fd-ebf05288e19a.png)


Allow(許可)をクリック
![スクリーンショット 2024-01-01 3.49.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/76876e82-a254-a097-f499-9c724e97962a.png)


これでデプロイ完了です
次で使うのでURLをコピーしてください
![スクリーンショット 2022-05-14 20.01.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/7849767e-89c4-3228-6eb0-b1a84b902a8e.png)

## デプロイすると発行されるURLをLINE Developersで設定します
再びLINE Developersに戻ります

Webhook URLを編集し、先ほどのURLを設定します
![スクリーンショット 2022-05-14 20.01.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/78186ca7-9fc8-d938-56cb-23db192e238f.png)
![スクリーンショット 2022-05-14 20.01.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/08175b8a-00f5-be0e-ed7e-63f974cce1aa.png)

検証を押して、成功と出たらOKです
![スクリーンショット 2022-05-14 20.01.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/ae44f53d-4c2b-1efe-20a8-cf894a741c90.png)

Webhookの利用をONにします
![スクリーンショット 2022-05-17 18.05.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/3a140b03-5c1b-6ba8-2667-423b3b1b3265.png)

LINE Developters に戻り、QRコードを読み込んでBotと友達になり、メッセージを送ってみるとそのまま返却してくれたら成功です！

![スクリーンショット 2022-05-15 1.11.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/78faafc0-56f7-19a3-5abe-f9a89c1fc56a.png)

## 補足
### ソースコードに変更を加えたら
ソースコードに変更を加えたら再度デプロイが必要です
![スクリーンショット 2022-05-17 18.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/aea2d645-60ee-2daf-a0d5-e4a617b9ad5d.png)

デプロイ > デプロイを管理 > 新バージョンを選択してデプロイ（URLは変更されないので、LINEDevelopersで操作は不要です）
![スクリーンショット 2022-05-17 18.04.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/7853aa22-806f-29a0-7605-e2bbcff8288c.png)

# AIの設定

## API Keyを発行

### openAIのアカウントを作成します
まだ登録しない人は、 https://openai.com/ にアクセスし、
右上の「Menu」から「Sing Up」をクリックしてアカウントを作成しましょう

:::note warn
新しくアカウントを作成する場合、クレジットカードの設定を行わないとAPIの利用ができないようになっています（2023/5/20現在）
設定をしておきましょう
:::
![スクリーンショット 2023-05-20 17.42.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/8642f772-c709-8e40-a272-b8722b3aa561.png)


### openAIにログインします
https://platform.openai.com/overview
![スクリーンショット 2023-05-20 17.13.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/620d1d5e-cc6f-6cd3-3327-c147e9856b5a.png)

### API Keys をクリック
![スクリーンショット 2024-01-01 3.52.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/a5146af4-76e2-862d-a5cb-1adad7c6b349.png)


### API keyを作成
![スクリーンショット 2023-05-20 17.18.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/61feb5db-a909-191b-d22a-c8afb9f81871.png)

![スクリーンショット 2023-05-20 17.20.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/e0c6eab2-1299-a79e-5f76-aa4299a4ba1e.png)

API Keyが発行されるのでコピーして控えておきましょう
![スクリーンショット 2023-05-20 17.21.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/1529f9d1-b13a-d956-60ac-6b2e14b96b56.png)

## GASのコードを編集
赤枠を編集します

![スクリーンショット 2024-01-01 3.54.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/eeb2eb40-573b-f0c8-09e2-bce1be2cd9cb.png)


```js
// AI設定
const openAIApiKey = "openAIから取得したAPIキーで書き換えます";
```

messages の中身を忘れずに更新してデプロイしましょう
![スクリーンショット 2024-01-01 3.55.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/980cfb06-5b2d-4069-cd79-a4c5ed501583.png)


:::note
設定お疲れ様でした！これで完成です！動作を確認してみましょう
:::
![スクリーンショット 2024-01-01 3.40.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/393aa5d2-6984-e56e-6ee6-2399e9359399.png)



# コードの解説
```js

//ポストで送られてくるので、ポストデータ取得
function doPost(e) {

  // ① ========================================
  // 動作確認用のログ出力
  // log_to_sheet("A", "doPost")

  // ② ========================================
  // LINEBotから送られてきたデータを、プログラムで利用しやすいようにJSON形式に変換する
  json = JSON.parse(e.postData.contents);

  // ③ ========================================
  //返信するためのトークン取得
  replyToken= json.events[0].replyToken;
  if (typeof replyToken === 'undefined') {
    return;
  }

  // ④ ========================================
  // 返信するメッセージを作成
  // オウム返しができるようになったら、message = test_message()をコメントアウトし、messages = getAIImageAnswer(json.events[0].message.text)をコメントイン
  messages = test_message()
  // messages = getAIImageAnswer(json.events[0].message.text)

  // ⑤ ========================================
  // line-bot-sdk-gas のライブラリを利用しています ( https://github.com/kobanyan/line-bot-sdk-gas )
  const linebotClient = new LineBotSDK.Client({ channelAccessToken: CHANNEL_ACCESS_TOKEN });

  // メッセージを返信
  try{
    linebotClient.replyMessage(replyToken, messages);
  }catch(e){
    // うまく動作しない場合は、エラーが発生していないか確認してみましょう
    // log_to_sheet("A", e)
  }


  // ⑥ ========================================
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

// 動作確認用のオウム返しのメッセージを作成する関数
function test_message() {
  //送られたLINEメッセージを取得
  var user_message = json.events[0].message.text;  

  //送られたメッセージをそのままオウム返し
  var reply_messages = [user_message];
 
  // メッセージを返信
  var messages = reply_messages.map(function (v) {
    return {'type': 'text', 'text': v};    
  });

  return messages
}

function getAIImageAnswer(text){
  // LINEBotに送られてきた文言でDALL-Eに画像の作成を依頼する。
  // textをそのままで画像を作るのではなく、画像生成に関する指示を足すと特徴的なLINEBotになります。
  // LINEBotから画像を送信するには画像のURLが必要なので、imageURLとして受け取る
  var imageURL = generateImageURL(text)
  // リプライメッセージを組み立てる
  messages = [
    {'type':'text', 'text': '画像ができました！'},
    {'type':'image', 'originalContentUrl': imageURL, 'previewImageUrl': imageURL}
  ]
  return messages
}

// DALL·E APIを呼び出し、出力結果の画像のURLを取得する関数
// 他にもいろいろパラメーターを設定することができるので、カスタマイズするには公式ドキュメントを読みましょう
// https://platform.openai.com/docs/api-reference/images
function generateImageURL(text) {
  // リクエストを送信するためのオプションを設定する
  var options = {
    "method": "post",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + openAIApiKey
    },
    "payload": JSON.stringify({
      "prompt": text,
      "size": "256x256",
      "response_format": "url"
    })
  };
  // リクエストを送信する
  var response = UrlFetchApp.fetch(DALLE_API, options);
  var data = JSON.parse(response.getContentText());
  // 画像のURLを取得する
  return data.data[0].url;
}

// 処理の確認用にログを出力する関数
function log_to_sheet(column, text) {
  if(logSheet.getRange(column + "1").getValue() == ""){
    lastRow = 0
  } else if(logSheet.getRange(column + "2").getValue() == ""){
    lastRow = 1
  } else {
    var lastRow = logSheet.getRange(column + "1").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
    // 無限に増えるので1000以上書き込んだらリセット
    console.log("lastRow", lastRow)
    if(lastRow >= 1000){
      logSheet.getRange(column + "1:" + column + "10").clearContent()
      lastRow = 0
    }
  }
  var putRange = column + String(lastRow + 1)
  logSheet.getRange(putRange).setValue(text);
}

```

## ① log_to_sheet("A", "doPost")
GASでLINEBotを作る際につまづきやすいところとして、エラーが起きたときに何が起きてるのかわからない！というのがあります。
それを解消するために、スプレッドシートに「log」というシートを用意し、今どこまで処理が進んでいるのか？というのを調べるために、log_to_sheetという関数を用意しました。
```js
log_to_sheet("出力する列", 出力する内容)
```
という内容で呼び出すと利用でき、
```js
log_to_sheet("A", "doPost")
```
だと、シート「log」のA列の一番上の空いているセルに"doPost"と書き込むことができます。
これを出力していると、LINEBotが動かない時に、そもそもLINEBotからGASに処理が飛んできているのか？という判定に使え、doPostという文言が出力されていないと、「LINEDevelopersに貼り付けたWebhookURLが間違っていないか？」と効率よく検証をすることができます。

## ② json = JSON.parse(e.postData.contents);

[LINE 公式のMessaging API](https://developers.line.biz/ja/docs/messaging-api/overview/)から画像を拝借しました。
![messaging-api-architecture.f40bffbb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/9e5856f0-2378-11ea-3885-a9bd773074aa.png)

この記事では、この画像の右側のYOUR SYSTEMの部分をGASで作っています![スクリーンショット 2023-01-02 2.22.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/9a4ba1df-308f-dbee-3bd2-102589463c94.png)
　LINEから文字が入力された際、「入力された文字」は "e.postData.contents" という変数の中に入っています。
そのほかに、リプライトークンと呼ばれる「特定の相手に返信をするための合言葉」も入っていて、それらをプログラム上で扱いやすいように、JSONと呼ばれるデータ形式に変換します。

## ③ 返信するためのトークン取得
リプライトークンと呼ばれる「特定の相手に返信をするための合言葉」は、先ほど変換したjsonのデータから以下のように取り出せます。
```js
replyToken= json.events[0].replyToken;
```

もし何らかのアクシデントでリプライトークンが入手できなかった場合は、その後の処理が無駄になるので、ここで処理を中断させます
```js
  if (typeof replyToken === 'undefined') {
    return;
  }
```

## ④ 返信するメッセージを作成
返信するメッセージは以下の形式にする必要があります
```js
[{'type':'text', 'text':'メッセージ'}]
```
参考：[テキストメッセージの公式リファレンス](https://developers.line.biz/ja/reference/messaging-api/#text-message)
スタンプなど他の形式で送りたい場合は、[公式リファレンス](https://developers.line.biz/ja/reference/messaging-api/#message-objects)で調べてみてください

送られてきたメッセージをそのままオウム返しにするtest_messageという関数を用意しているので
```js
messages = test_message()
```
と書けば、変数messagesに送られてきたメッセージがそのまま詰められた返信メッセージのデータが挿入されます。自分用のプログラムを書く時にも返信する内容を以下のようにしてあげればOKです。
```js
[{'type':'text', 'text':'メッセージ'}]
```

## ⑤ line-bot-sdk-gas のライブラリを利用してクライアントを生成
前回の記事([5分でつくるLINEBot](https://qiita.com/drafts/0d121ee6d48a8c1634fc))から大きく変えた点です。
[LINE Messaging API を Google Apps Script で利用するためのライブラリ作りました](https://qiita.com/kobanyan/items/1a590cda9deb85e86296)の記事で紹介されているライブラリをスプレッドシートに組み込んでいます。

このライブラリを使うことで、返信をするためのコードがグッと少なくなります
```js:ライブラリなし
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
  'headers': {
    'Content-Type': 'application/json; charset=UTF-8',
    'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
  },
  'method': 'post',
  'payload': JSON.stringify({
    'replyToken': replyToken,
    'messages': messages,
  }),
});
```

```js:ライブラリあり
const linebotClient = new LineBotSDK.Client({ channelAccessToken: CHANNEL_ACCESS_TOKEN });
linebotClient.replyMessage(replyToken, messages);
```

### そもそもライブラリとは？
GASでは、作ったプログラムを他の人が利用できるようにする機能があり、先ほどの記事で紹介されていたライブラリを組み込んで、このスプレッドシートでも利用できるようにしています。
このline-bot-sdk-gasというライブラリは、Node.jsで利用できる「SDK」（ソフトウェア開発キット）と同じようなことがGASでも出来るように作られているため、[Messaging API公式リファレンス](https://developers.line.biz/ja/reference/messaging-api/#send-reply-message)のNode.js用のサンプルコードを参考に開発を進めることができます
![スクリーンショット 2023-01-02 2.55.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/ff41ba94-420a-591f-0d88-b7b42ff26a30.png)

実際に使える関数の詳しい内容は[こちら](https://github.com/kobanyan/line-bot-sdk-gas#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)から確認してください。

## ⑥ 関数の終了をLINEBotに伝える
LINEBotに処理が終わったことを伝えます。ここは特に変更が必要になることはないと思います。
```js
return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
```

# おわり
以上、簡単にLINEBotを作るやり方の紹介でした！