【業務効率化】GoogleSheetAPIとJavaScriptで社内検索botを作ってみた


## こういう人に向けて書いてます

本記事には、

・面倒くさい事務作業を減らしたい人
・会社のために、何かを作りたいけど、バックエンドが苦手な人
・みんなが使えるツールを作りたい人

に向けて書いています。

## なぜ社内検索botを作ったのか？

去年、ジーズアカデミーに入学して、プログラミングを学ぶきっかけの一つっは、どうしても解決したい会社での小さな問題であった。それは、社内でいつも発生する、`カスタマー対応の煩雑さ`。

流れはこちら：
メールが読み→該当するテンプレを探す→お客様の名前を書き換わる→自分のお名前を書き換わる→確認する→送信

~~（自分の業務じゃないけど、だるい！）~~

ここで考えたのが：

１、全社にテンプレ共有
２、書き換わる部分をわかりやすくする。ミスの少なくする。

## 目的
・問い合わせメールをもっと早く返したい
・テンプレを探す手間を省きたい
・担当不在の時でも、情報共有によって、誰でもある程度業務を引き継げられる環境作りたい
・顧客対応関連で、社内で小さな衝突をなくしたい
・内容更新も簡単で、誰でも使える簡単なシステムにしたい（めちゃくちゃ重要）

## こんなものです！

![chatbot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959760/7a904ae3-36bb-79a3-8991-c4a1dace91df.gif)

- [x] 選択式で、親カテゴリーから絞っていき、該当テンプレを探し出す。

- [x] 担当スタッフの名前を選択

- [x] お客様のお名前を入力

- [x] 検索ボタンをクリックすると同時に、スタッフ、お客様の名前が入った状態でテンプレを出す。

- [x] コピーボタンを押してから、メールを貼り付けて完了。

- [x] 探す手間も、全選択（Ctrl+A)の手間も省けます。

## 仕組み

JavaScriptからfetch関数を使って、スプレッドシートの情報を取得
整列して、ブラウザに表示する。

![無題のプレゼンテーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959760/c4010fd9-01b4-a59a-c018-35250ba8eab1.png)

### Google Cloud Platform (GCP）の設定

前回の記事で、LaravelでGoogleSheetAPIを使ってみたが、今回はサーバーなど関係なし、ローカルで使っていただくことになりますので、比較的にセキュリティーへの懸念が少なく、APIキーを取得するやり方にしました。

サービスアカウントのやり方に興味ある方はこちらの記事へ ^^

https://qiita.com/guolam/items/ccb1e68224584807cb4b

設定方法はまず、GCPで`API / サービス詳細`ページで、SheetAPIキーを有効にする

![スクリーンショット 2023-09-09 1.12.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959760/6106d914-e235-c138-915a-817425754e10.png)

そして、`認証情報`ページに、APIキーを生成。大事なのは、必ず`キーの制限`を設定すること。設定しない場合、動かなかったりします > <


### JavaScriptでデータを取得する

次は、API経由で、スプレッドシートの情報を取得する。
そして、取得したスプシのjsonデータを配列にする。

```
  const apiKey = // あなたのAPIキーに置き換える
  const spreadsheetId = // あなたのスプレッドシートのIDに置き換える
  const sheetName =  // データがあるシート名に置き換える

  const endpoint = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${sheetName}?key=${apiKey}`;

  // endpointからデータを取得する
  fetch(endpoint)
    .then(response => response.json())
    .then(data => {
      const qaPairs = data.values.slice(1).map(row => ({
        course: row[0],
        category: row[1],
        question: row[2],//同じコードを使い回ししていますので、ここを保留しています。
        answer: row[3]
      }));
```

あとは取得したデータを処理する。

**やることが主に3つ**

1. セレクトボックスを並び替えた状態で表示
2. 選んだ結果を`textarea`で表示
3. コピー機能つつける

#### 1 セレクトボックスを並び替えた状態で表示

HTMLタグにあるデータを取得し、セレクトボックスの中身を綺麗に整理する。

データ取得
→カテゴリーなど並べ替え
→セレクトボックスのオブションを更新

```
// データを取得したら、検索ボタンを有効にする
      const searchBtn = document.getElementById("searchBtn");

      // セレクトボックスを取得
      const categorySelect = document.getElementById("categorySelect");
      const courseSelect = document.getElementById("courseSelect");

      // カテゴリーのオプションを追加
      const queryInput = document.getElementById("query");
      const resultsDiv = document.getElementById("results");

      // カテゴリー、コースのオプションを追加
      const categories = [...new Set(qaPairs.map(qaPair => qaPair.category))];
      const courses = [...new Set(qaPairs.map(qaPair => qaPair.course))];

      // カテゴリーやコースのオプションを昇順に並び替え
      const sortedCategories = categories.sort();
      const sortedCourses = courses.sort();

      // カテゴリーセレクトボックスのオプションを更新
      categories.forEach(category => {
        const option = document.createElement("option");
        option.value = category;
        option.textContent = category;
        categorySelect.appendChild(option);
      });

      // コースセレクトボックスのオプションを更新
      courses.forEach(course => {
        const option = document.createElement("option");
        option.value = course;
        option.textContent = course;
        courseSelect.appendChild(option);
      });

  // コース選択時に関連するカテゴリーオプションを更新する関数
      function updateCategoryOptions(relatedCategories) {
        // カテゴリーセレクトボックスのオプションを一旦クリア
        categorySelect.innerHTML = '<option value="">すべてのカテゴリー</option>';

        // 重複を除去して昇順にソート
        const uniqueCategories = [...new Set(relatedCategories)].sort();

        uniqueCategories.forEach(category => {
          const option = document.createElement("option");
          option.value = category;
          option.textContent = category;
          categorySelect.appendChild(option);
        });
      }
```

#### 2 選んだ結果を`textarea`で表示

ここは一番簡単だけど、ちゃんとmarginもここで調理できるのはチャッピが教えてくれたおかげです。

```
            const spacingDiv = document.createElement("div");
            spacingDiv.style.marginBottom = "20px"; // 適切な間隔に調整

            const separator = document.createElement("hr");
            separator.style.margin = "20px 0"; // 適切な間隔に調整

            resultsDiv.appendChild(courseDiv);
            resultsDiv.appendChild(spacingDiv);
            resultsDiv.appendChild(categoryDiv);
            resultsDiv.appendChild(spacingDiv);
            resultsDiv.appendChild(answerDiv);
            resultsDiv.appendChild(answerTextarea); // テキストエリアを追加
            resultsDiv.appendChild(copyButton);
            resultsDiv.appendChild(separator);
```

#### 3 コピー機能つつける

コピーボタンは割と検索したらすぐ出てきますが、今回面倒くさかったのが、変数で置き換えたスタッフ名とお客様の名前をコピーするので、```replaceVariables```の関数で、置き換えたものを取得する。~~ここに気づくまで、3時間溶けました~~



```
      // コピーするボタンの作成
            const copyButton = document.createElement("button");
            copyButton.textContent = "回答をコピー";
            copyButton.addEventListener("click", () => {
              const answerText = replaceVariables(result.answer); //←ここが重要!! 
            //const answerText = result.answer; // ←最初これにして、詰んだ ^^;;;
              const textArea = document.createElement("textarea");
              textArea.value = answerText;
              document.body.appendChild(textArea);
              textArea.select();
              document.execCommand("copy");
              document.body.removeChild(textArea);
              alert("回答がコピーされました！");
            });
```

分類や質問や回答からキーワード検索
もしくは分類から検索


### スプシを記入
スプレットシートはとても簡単。コース、カテゴリー、答えを設定します。

![スクリーンショット 2023-09-09 0.56.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959760/c1a52a54-a3e3-f12d-eee5-65f519da2b31.png)

そして、`{staff_select}`と`{customer}`を表示させたい場所に設定すれば、完成 ＼^0^/


## 今回の件で実現できたこと(&伝えたいこと)

オフラインでの社内共有なので、分類ができれば、意外とすぐ実現しちゃう。

逆に、テンプレの取捨選択が難しかった。汎用性の高いテンプレを使うが、使えないものをどう処理するのか、その基準を考えるのに、1日かかりました。

そして、プログラミングを勉強した時、技術を使いたい欲があったが、時間が経ち、誰のために、何を作るにフォーカスできたら、すごいモノを作るより、誰でも触れる、誰でも改善できるモノがいいに気づいた。だから、誰でも更新できるスプレッドシートにしました。スプレッドシートをどんどん更新していけば、テンプレが貯めていける仕組みとなります。（やさしい世界！）

最後に、非エンジニアで、めちゃくちゃコードを書けるわけじゃないけど、限られてる能力で、少しでも身近にある面倒なことを減らせるのも幸せやん〜

という、今回の気づきでした。おしまい〜