ChatGPTのAPIをGASで叩き、SlackBotで会話できるようにする

# はじめに
最近GASなる存在を知った私、あまりに何でもできることに気が付き、朝起きては夜寝るまで毎日GASのことについて考える ~~変態~~ 人間になってしまいました。どうも @Keichan_15 と申します。

そういえば最近 **ChatGPT API** が公開されましたね！

https://twitter.com/gdb/status/1630991925984755714?s=46&t=ishdZl62YaixgqciZeBeVw

```GPT-3.5-turbo``` と呼ばれるモデルを新たに採用し、価格は1,000トークン辺り0.002ドルという破格の値段設定。
同様に Whisper と呼ばれる音声合成モデルについてもAPIが提供開始されたようです！(私は Whisper については未キャッチアップ分野…。ちょっと触ってみたさはある)

そこで今回は、このできたてホヤホヤのChatGPT APIを **GASから叩いてみようぜ！** さらに飽き足らず、**そのままSlackBotにしてみようぜ！** といった記事になります！

ここ数週間でGASをガンガン触っているものの、JavaScriptを書くこと自体がほとんど初めてであるが故かなり変なコードになっています…。ビシバシコメント欄にてご指導頂けますと幸いです！:bow:

# 完成品
![2023-03-04 (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/036b5807-2bbc-19e8-3ed2-f0ac073bf246.jpeg)
![2023-03-04 (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/d6494229-9137-9d7b-7c29-26b09f89c30c.jpeg)

このように、あるチャンネル内で質問したい内容をメッセージで投稿すると、そのメッセージのスレッドにChatGPT_Bot君が答えてくれるよ！といった内容です。

大まかな流れとしては、
1. 事前準備(SlackBot用のアプリ作成 & APIキー取得)
2. Outgoing Webhookで投稿したメッセージを取得
3. GAS側で、2.で取得したメッセージを設定したうえでAPIにリクエスト
4. レスポンスの内容から、ChatGPTの返答内容が記載されている部分を抜き出し、Slack App(ライブラリ)でスレッドにBotがメッセージ送信

となります。もう少しシンプルにできる方法ってあるのかな…？:rolling_eyes:

# 1. 事前準備
## 1-1. Slackアプリの作成
まずはSlackBotのアプリを作成します！ [SlackAPIのページ](https://api.slack.com/apps) にアクセスし、画面中央に表示されている **`Create an App`** をクリックします！

https://api.slack.com/apps

![2023-03-04 (22).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/d427a275-98ab-1955-f9b8-3dceec31b80c.png)


::: note warn
既にWorkSpace内で別のアプリケーションが存在する場合は、**`Create New App`** をクリックしましょう！

![2023-03-04 (5).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/552aca6f-4d13-a3a6-ef44-9696a0ef9067.jpeg)


:::

クリックすると下記の画面が表示されるので、**From scratch** をクリックします！

![2023-03-04 (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/6b9def7e-90b5-6338-37b7-cf7435e13180.png)

次に下記の画面が表示されると思います！
作成するアプリ名を **App Name** の欄に入力しましょう！(Botの名前になるヨ)
併せて作成したアプリを導入するworkspaceを **Pick a workspace to develop your app in:** のプルダウンから選択してください！

どちらも入力が完了すれば **`Create App`** のボタンが <font color="green">**緑色**</font> に変わったのを確認し、クリックを押しましょう！
※今回はApp NameをChatGPT_Bot、導入するworkspaceはtestとします。

![2023-03-04 (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/3ae96619-1ae0-010d-427e-6b64a11a05f4.png)

次に権限周りの設定を行います！
左の一覧の中から、 **OAuth & Permissions** を探し、クリックしましょう！

![2023-03-04 (15).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/a3ac4e7b-de5b-f8b0-a06c-827e893a923a.jpeg)

**`Scopes`** まで画面をスクロールしてください。今回はBotからメッセージを送りたいので、**`Bot Token Scopes`** を編集していきます。
今回追加するScopeは2つです。`Add an OAuth Scope` のボタンをクリックし、下記の2つのScopeを追加しましょう！

- **channels:history**
- **chat:write**

追加後、下記のような表示になっていればOKです！

![2023-03-04 (23).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/bec095ac-4495-b784-eeed-7efbf0739e21.png)

最低限の設定が完了したので、workspaceと接続します。
上にスクロールして頂くと、 **OAuth Tokens for Your Workspace** の項目があると思います。その画面内にある **`Install to Workspace`** をクリックしてください！

![2023-03-04 (24).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/071f14f8-bb2b-ce53-b04e-d4a46c5f8584.png)

内容を確認し、**`許可する`** をクリックします！

![2023-03-04 (25).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/7a77fdf0-4a36-bb76-9190-9c947f29f2bf.png)

下の写真のように **Bot User OAuth Token** が発行されていればOKです！

![2023-03-04 (18).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/089f1dcd-9f74-69ee-5ba7-759510af9a31.jpeg)


このTokenは後に必要になりますのでメモしておいてくださいね！

## 1-2. OpenAI のAPIキー取得
OpenAI のAPIキー取得にはchatGPTのアカウントが必要になります。アカウント作成方法は下記の記事をご覧ください。
※今回はアカウントを作成していることを前提で進めていきます。

https://qiita.com/k_suzuki527/items/389a9815b03041617d9e

まず[こちら](https://platform.openai.com/overview)にアクセスします。

https://platform.openai.com/overview

![2023-03-04 (54).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/03a9e2d6-7929-9ce4-8d83-0799a5825102.png)

写真のような画面に遷移します。`Log in` が済んでいなければログインしておきましょう。

ログインすると右上に自身のアカウントが表示されます。クリックすると写真のような一覧が表示されるので、その中の **`View API keys`** をクリックしてください。

![2023-03-04 (53).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/40dfc07a-15e6-f37f-3fc4-dae9541dda7f.jpeg)

以下の写真のような画面が開いたら、 **`+ Create new secret key`** をクリックしてください。
APIキーが発行されるので、このAPIキーもメモしておきましょう！

![2023-03-04 (56).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/ddf88df3-1d0e-1479-b3ee-558a14ac7e7e.png)

以上で事前準備は完了です！

# 2. Outgoing Webhookの準備
簡単にOutgoing Webhookについて解説します。Slack APIのドキュメントには以下のように記載されています。

https://api.slack.com/legacy/custom-integrations/outgoing-webhooks

>**Outgoing Webhooks**
>
>*Outgoing Webhooks are a legacy method of sending notifications to an app about two specific activities:*
>
>*1. A message was posted in a particular public Slack channel.*
>*2. A message was posted in any public Slack channel containing specific trigger words.*

要するに、特定のキーワードを含めたメッセージが送信された際に、外部サーバーやアプリにその情報を転送することができます。
今回であれば、 **最初にユーザーが入力した文章全部をキーワード** としてメッセージの情報をGASへ送ってあげる必要があります。

早速設定していきましょう！

まずは1.で作成したSlackアプリを導入したworkspaceを開きます。
workspaceの右横の **`v`** をクリックし、``設定と管理 -> アプリを管理する`` をクリックしてください。

![2023-03-04 (20).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/b6d3eafa-f907-8b84-8ca8-f8128c142c6a.jpeg)

下記のような画面が出ればOK！
![2023-03-04 (26).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/c7ff03d4-a1ce-b807-1339-139f828d251a.png)

次に右上のフォームに **_Outgoing Webhook_** と入力してください！

![2023-03-04 (28).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/3d2c171c-591f-256a-26a5-27c3624dc8a8.png)

**``Slackに追加``** をクリック！

![2023-03-04 (31).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/375b34ec-e98c-c0db-051a-bf2fd78f8f14.png)

**``Outgoing Webhook インテグレーションの追加``** をクリック！

![2023-03-04 (33).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/84f308a9-d97f-3184-6291-aa0a6af9e5ae.png)

このような画面が出ればOKです！

![2023-03-04 (35).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/0027f012-f015-0416-6753-a38dccc6eaaa.png)

こちらのページは後ほど使用しますので、ブックマークに登録するなどして消さないようにしておきましょう！

# 3. GAS
それでは本題のGASに移っていきましょう！内容が多いため、4章に分けて解説します。

## 3-1. GASの準備

まずはGASを開きます！
今後スプレッドシートと連携する可能性を考慮し、スプレッドシートの拡張機能タブからGASを開くことをおススメします！

https://www.google.com/intl/ja_jp/sheets/about/

新しいスプレッドシートを開き、`拡張機能`のタブから **Apps Script** をクリックします！

![2023-03-04 (38).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/09defc29-d941-627d-d6a6-787a86516fe8.png)

GASが開けばOKです！

![2023-03-04 (41).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/cd361889-d26f-c2be-a7da-789c5f20ad0f.png)

## 3-2. Slack Appライブラリの追加
次にライブラリを追加します。
今回はGASからSlackのチャンネル内にメッセージを送る為に、Slack Appと呼ばれるライブラリを活用します。

https://qiita.com/soundTricker/items/43267609a870fc9c7453

左側の `ライブラリ` 横の `+` をクリックし、下記のスクリプトIDを入力して検索します！

**`1on93YOYfSmV92R5q59NpKmsyWIQD8qnoLYk-gkQBI92C58SPyA2x1-bq`**

![2023-03-04 (45).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/e054bc48-44b4-be07-db51-b232f27dda0b.png)

上の写真のように表示されれば **`追加`** ボタンをクリックしてください！
これでSlack Appライブラリの導入は完了です。

## 3-3. スクリプトプロパティの設定
次にスクリプトプロパティを設定します。スクリプトプロパティはkeyとvalueの対でデータを保管します。
SlackBotのTokenやAPIキーなどをGASのコードに直接入力するとセキュリティの観点から望ましくないことから、このスクリプトプロパティが用いられます。

左側の歯車(プロジェクトの設定) マークをクリックし、「プロジェクトの設定」画面を開きます。
一番下までスクロールすると、**スクリプト プロパティ** の項目が表示されるので、**`スクリプトプロパティを追加`** ボタンをクリックします。

![2023-03-04 (47).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/5ea61eeb-2de3-bd50-3992-ba73d9b323e8.png)

![2023-03-04 (49).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/a334b005-ff4e-eff6-3475-92842560241c.png)

このように入力画面に切り替わります。今回は2つスクリプトプロパティを追加します。
- プロパティ: SLACK_BOT_TOKEN 値: 1-1でメモしておいたSlackアプリのToken
- プロパティ: OPEN_AI_KEY 値: 1-2でメモしておいたOpenAIのAPIキー

![2023-03-04 (58).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/cb2a182e-03e4-7c3b-da6f-771fa7f1bf62.jpeg)

このように設定できればOKです！

## 3-4. GASでAPIを叩いてみる
API ReferenceのCurlコマンドでのrequestを参考にコードを作成していきます。
事前にcurlで叩いてみて返答が返ってくるか確認してみると正確だと思います。

https://platform.openai.com/docs/api-reference/chat/create

早速chatGPT APIをGAS側から叩いて、レスポンスが問題無く返ってくるか確認してみます。
コード.gsを下記のように編集してください。

``` javascript:コード.gs
const prop = PropertiesService.getScriptProperties().getProperties();

function doTest() {

  let app = SlackApp.create(prop.SLACK_BOT_TOKEN);

  const apiKey = prop.OPEN_AI_KEY;
  const url = "https://api.openai.com/v1/chat/completions";

  const payload = {
    "model": "gpt-3.5-turbo",
    "messages": [{"role": "user", "content": "Hello!"}]
  };

  const options = {
    "method": "post",
    "contentType": "application/json",
    "headers": {
      "Authorization": "Bearer " + apiKey
    },
    "payload": JSON.stringify(payload)
  };

  const response = UrlFetchApp.fetch(url, options);
  const content = JSON.parse(response);
  
  Logger.log(content);

}
```

処理内容としては ``doTest()`` 関数を定義し、「Hello!」とメッセージを送った際のAPIのレスポンスを `content` 変数に格納しています。その後、`Logger.log(content);` で `content` の中身をログ出力しています。

コードを書き終えたら、上の **`実行`** ボタンをクリックしましょう。

![2023-03-04 (60).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/0e863b5f-fcba-4700-b9f5-653617cae2eb.png)

初回実行時は下記のようなアラートが出ると思います。 **`権限を確認`** ボタンをクリックします。

![2023-03-04 (62).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/996af014-0087-e928-9cf7-8d2d3ce51d42.png)

自身のアカウントをクリック。

![2023-03-04 (64).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/33714b85-91af-c7d0-0988-91436f57a912.jpeg)

左側の `詳細` をクリックし、`(安全ではないページ)に移動` をクリック。

![2023-03-04 (66).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/dbaa8459-7390-ae61-ab67-6aae47c6449f.jpeg)

`許可` をクリック。

![2023-03-04 (68).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/4b61f2f8-9a9e-2fe8-6816-63c07c88d846.jpeg)

記述に問題が無ければ、下記のようなログが出力されることが確認できると思います。

![2023-03-04 (72).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/0c0ed451-8430-2ae5-9f54-9826c85509fd.png)

ここまで完了すればGASでAPIを叩く編は終了です！お疲れ様でした！

ここから先はChatGPTのSlackBot作成に移っていきましょう！！

出力されるログの内、chatGPTが質問内容を受け取って返信している項目は、`message` の `content` に入っていることが分かります。この `content` 部分を取り出し、返信時のメッセージとして当ててあげれば、よしなに動いてくれそう…。

ということで、コードを少し弄ります。

``` javascript:コード.gs
const prop = PropertiesService.getScriptProperties().getProperties();
const lock = LockService.getScriptLock();

function doPost(e) {

  let app = SlackApp.create(prop.SLACK_BOT_TOKEN);
  let message = e.parameter.text;
  let userID = e.parameter.user_id;

  if (userID === prop.BOT_ID) return;

  lock.tryLock(10000);
  
  const apiKey = prop.OPEN_AI_KEY;
  const url = "https://api.openai.com/v1/chat/completions";

  const payload = {
    "model": "gpt-3.5-turbo",
    "messages": [{"role": "user", "content": message}]
  };

  const options = {
    "method": "post",
    "contentType": "application/json",
    "headers": {
      "Authorization": "Bearer " + apiKey
    },
    "payload": JSON.stringify(payload)
  };

  const response = UrlFetchApp.fetch(url, options);
  const content = JSON.parse(response);

  // contentオブジェクトにAPIから返された情報が含まれる
  const message2 = content.choices[0].message.content;

  app.postMessage("your channel_name", message2, {
    thread_ts: e.parameter.timestamp
  });

  lock.releaseLock();

}
```

`BOT_ID` はBOTのユーザーIDを前述のスクリプトプロパティで組み込んでいます。
``メンバーID`` をコピーして頂き、**`BOT_ID`** というプロパティ名でスクリプトプロパティを設定してください。

確認方法ですが、Slackのworkspaceに導入されたBotをDMから開きます。

![2023-03-04 (75).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/8ef98827-5c99-3095-d53b-e092a413a673.png)

![2023-03-04 (74).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/211d1ef6-8e9f-58ec-3bb6-5390b91ff354.jpeg)

この中の **メンバーID** の部分が `BOT_ID` となります！忘れずコピーしてGASに貼り付けましょう！

さらにここで重要なポイントが2つあります！

### ポイント① 

```javascript:コード.gs
if (userID === prop.BOT_ID) return;
```

今回の実装ではOutgoing Webhookのトリガーワードを空白にしているため、送信したメッセージ全てをトリガーとしてGASに送っています。そのため、上記の記述が無いとBotの返信をトリガーで受け取って、更にその返信を受け取って…といった無限ループが発生します。 ~~僕はこれで大多数の方にご迷惑をお掛けすることに…~~

### ポイント②

Slack Appライブラリの `postMessage()` を使用してメッセージを送信します。第1引数には送信したいチャンネル名、第2引数には送信したいメッセージを設定します。

```javascript:コード.gs
  app.postMessage("your channel_name", message2, {
    thread_ts: e.parameter.timestamp
  });
```

``your channel_name`` には導入したいSlackのチャンネル名を入力してください。例えばbotというチャンネルに導入したい場合は下記のようになります。

```javascript:コード.gs
  app.postMessage("#bot", message2, {
    thread_ts: e.parameter.timestamp
  });
```

ここまで実装完了したらアプリケーションをデプロイします。

画面右上の `デプロイ` をクリック → `新しいデプロイ` をクリック → 歯車マークから `ウェブアプリ` を選択 → アクセスできるユーザーを **必ず** **`全員`** に変更してデプロイ

![2023-03-04 (78).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/ece6956d-74d0-37f0-6343-c2fb73aaf601.png)

![2023-03-04 (81).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/7909381d-3d87-d989-fe16-affb9a294e1b.png)

![2023-03-04 (85).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/cad24462-ada2-cf64-ad0c-0e021463e6f2.png)

![2023-03-04 (84).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/dc7fcfbf-7a38-b3fb-3f69-68a9809fc49a.jpeg)

![2023-03-04 (88).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/da3c0807-3671-77c1-0f36-96dbc03de510.jpeg)

デプロイ後、ウェブアプリのURLをコピーしてメモしておきましょう！

# 4. デプロイURLをOutgoing Webhookに設定
2.で設定したOutgoing Webhookのページに戻ります。

スクロールすると **インテグレーションの設定** の項目があると思います。

![2023-03-04 (90).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/4d87d7a2-7b3a-dfb4-20a3-b0ae773793f2.jpeg)

- チャンネル → メッセージを取得したいチャンネル
- URL → GASのデプロイURL

の2つを選択して **`設定を保存`** ボタンをクリックします。

これで全ての準備が整いました！

# 5. アプリをチャンネルに召喚！
Outgoing webhookで設定したチャンネルにアプリを追加します。

Slack上部にあるチャンネル横の **``V``** を選択し、 **インテグレーション** をクリックします。

![2023-03-04 (96).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/6d4eb2ea-f16b-fbcc-1da2-fe56ff95c680.png)

`アプリを追加する` をクリックし、Bot名を検索して **`追加`** をクリックします。

![2023-03-04 (95).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/b2e67a94-773b-654e-569e-e26bf306d13c.png)

あとは好きなように質問すると、Bot君がスレッドに返信してくれます！

![2023-03-04 (98).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/c112fc88-dd42-5a49-31f4-6000284a1286.png)

![2023-03-04 (99).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/96bf7be6-51d3-cf7d-9ab8-dc4147f64108.png)

以上です！お疲れ様でした～～！:v:

# おわりに
GASって本当に便利ですね…。逆に今まで活用しなかったことを悔やむレベルです。笑

また別件にはなるのですが、自動リアクションBot(リアクションするのはBotではなくユーザー自身)もGASで作成できたので近々記事にしていきたいと思います。

ひとまずChatGPT、末恐ろしいAIでした…。:sweat_smile:

おしまい。