🕛 2023.3.19 01:48

ChatGPT APIで自動要約するGoogle Chromeエクステンションの作り方

最近話題になっているChatGPTとチャットしてなにか作ってみたい(笑)

ChatGPT APIでニュースやブログの記事を自動的に要約してくれるGoogle Chromeエクステンションを作りたいと思います。

ChatGPT APIを作成

OpenAIのウェブサイトでアカウントを作成後、以下ページでAPIを取得できます。API作成後、ページからは確認できないので、APIキーをメモに残し安全に保管しておきましょう。

https://platform.openai.com/account/api-keys

Chromeエクステンションの基本設定

Google Chromeエクステンションの開発に必要な基本設定を行い、マニフェストファイル(manifest.json)を作成します。このファイルには、エクステンションの名称、バージョン、説明、アイコンなどの情報が含まれます。

{
  "manifest_version": 3,
  "name": "AutoSummarizer by ChatGPT",
  "version": "1.0",
  "description": "Automatically summarize selected text using ChatGPT",
  "permissions": [
    "contextMenus",
    "storage",
    "tabs",
    "scripting",
    "notifications"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "48": "icon.png"
    }
  },
  "options_page": "options.html",
  "icons": {
    "48": "icon.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "run_at": "document_start",
      "matches": [
        "<all_urls>"
      ],
      "js": [
        "contentScript.js"
      ]
    }
  ]
}

コンテンツスクリプトの作成

コンテンツスクリプトの作成: 要約機能を実装するためのコンテンツスクリプトを作成します。このスクリプトは、ウェブページ上で実行され、テキストの選択や要約結果の表示などを行います。

// DOMが読み込まれた後に実行されるイベントリスナーを追加
document.addEventListener('DOMContentLoaded', () => {
  // メッセージリスナーを追加
  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    // getSelectedTextアクションがリクエストされた場合
    if (request.action === 'getSelectedText') {
      const selectedText = getSelectedText();
      console.log(selectedText);
      sendResponse(selectedText);
    }
  });
});

// 選択されたテキストを取得する関数
function getSelectedText() {
  const selectedText = window.getSelection().toString();
  // 改行を空の文字列に置き換える
  const textWithoutNewlines = selectedText.replace(/\n|\r/g, ' ');
  return textWithoutNewlines;
}

ChatGPT APIとの通信

JavaScriptを使用して、ChatGPT APIに接続します。APIキーを使用して認証し、選択されたテキストを送信して要約をリクエストします。

ChatGPT APIからの要約結果を受け取り、適切な形式に整形します。要約結果をウェブページ上に表示するためのUIを作成し、要約結果をそのUIに挿入します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="css/style.css">
  <title>Document</title>
</head>
<body>
  <div id="container">
    <a id="optionsLink" href="options.html" target="_blank">
      <span id="gearIcon">⚙️</span>
  </a>
  <h1>AutoSummarizer by chatGPT</h1>
  <p>要約したい文書を選択して自動要約しましょう!</p>
  ※ 1,000 文字以下推奨</p>
  <button id="summarizeButton">自 動 要 約</button>
  <div id="loadingMark" class="spinner hidden"></div>
  <br> <!-- ここに改行を追加 -->
  <div id="loadingMessage" class="hidden">読み込み中...数分かかることもあります</div>
  <div id="summary" class="hidden"></div>
</div>
  <script src="popup.js"></script>
</body>
</html>

document.getElementById("summarizeButton").addEventListener("click", async () => {
  // 必要な要素を取得し、ローディングマークとメッセージを表示
  const summarizeButton = document.getElementById("summarizeButton");
  const loadingMark = document.getElementById('loadingMark');
  const loadingMessage = document.getElementById('loadingMessage');
  loadingMark.classList.remove('hidden');
  loadingMessage.classList.remove('hidden');
  summarizeButton.hidden = true;

  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  chrome.tabs.sendMessage(tab.id, { action: "getSelectedText" }, (selectedText) => {
    chrome.runtime.sendMessage({ action: "summarize", text: selectedText });
  });
});

// メッセージリスナーを追加
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  const hideLoading = () => {
    const summarizeButton = document.getElementById("summarizeButton");
    const loadingMark = document.getElementById('loadingMark');
    const loadingMessage = document.getElementById('loadingMessage');
    const summary = document.getElementById('summary');
    loadingMark.classList.add('hidden');
    loadingMessage.classList.add('hidden');
    summary.classList.remove('hidden');
    summarizeButton.hidden = false;
  };
  if (request.action === "displaySummary") {
    // 要約を表示する要素を取得
    const summaryElement = document.getElementById("summary");
    // 要約を表示する要素に要約を設定
    summaryElement.textContent = request.summary;
    hideLoading();
  }
  if (request.action === "displayError") {
    // エラーを表示する要素を取得
    const summaryElement = document.getElementById("summary");
    // エラーを表示する要素にエラーメッセージを設定
    summaryElement.textContent = request.error;
    hideLoading();
  }
});

バックグラウンドでAPIと通信します。

// メッセージリスナーを追加
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // summarizeアクションがリクエストされた場合
  if (request.action === "summarize") {
    // APIキーとモデル名をストレージから取得
    chrome.storage.local.get(['apiKey', 'model'], ({ apiKey, model }) => {
      // APIキーが存在しない場合、オプションページを開く
      if (!apiKey) {
        chrome.tabs.create({ url: "options.html" });
        return;
      }
      // 選択されたテキストがない場合、エラーメッセージを表示
      if (!request.text) {
        chrome.runtime.sendMessage({ action: "displayError", error: "文章が選択されていません" });
        return;
      }
      // OpenAI APIにリクエストを送信
      fetch("https://api.openai.com/v1/completions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${apiKey}`
        },
        body: JSON.stringify({
          model: `${model}`,
          prompt: `要約:\n${request.text}`,
          max_tokens: 2048,
          temperature: 0
        }),
      })
        .then(response => response.json())
        .then(data => {
          // APIから得られた要約を取得し、ポップアップに表示
          const summary = data.choices[0].text.trim();
          chrome.runtime.sendMessage({ action: "displaySummary", summary: summary });
        })
        .catch(error => {
          // エラーが発生した場合、エラーメッセージを表示
          chrome.runtime.sendMessage({ action: "displayError", error: error.message });
        });
    });
  }
  return true;
});

APIキーの取得

エクステンションのオプションページを作成し、ユーザーが自分のOpenAI APIキーを入力・保存できるようにします。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Options</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <h1>AutoSummarizer by chatGPT 設 定</h1>
    <h2>APIキーの設定</h2>
    <input type="text" id="apiKeyInput" placeholder="APIキーを入力してください">
    <h2>モデルの選択</h2>
    <select id="modelSelect">
      <option value="text-davinci-003" selected>text-davinci-003</option>
        <option value="text-davinci-002">text-davinci-002</option>
    </select>
    <button id="saveButton">保存</button>
    <div id="message"></div>
    <script src="options.js"></script>
</body>
</html>
document.addEventListener('DOMContentLoaded', () => {
  const apiKeyInput = document.getElementById('apiKeyInput');
  const modelSelect = document.getElementById('modelSelect');
  const saveButton = document.getElementById('saveButton');
  const message = document.getElementById('message');

  // 保存されたAPIキーを取得して表示
  chrome.storage.local.get(['apiKey', 'model'], ({ apiKey, model }) => {
    apiKeyInput.value = apiKey || '';
    modelSelect.value = model || 'text-davinci-003';
  });

  // 保存ボタンが押されたときの処理
  saveButton.addEventListener('click', () => {
    const apiKey = apiKeyInput.value;
    const model = modelSelect.value;
    chrome.storage.local.set({ apiKey, model }, () => {
      message.textContent = 'APIキーとモデルが保存されました';
    });
  });
});

エクステンションのテスト

Google Chromeを開き、右上のメニューから「その他のツール」>「拡張機能」をクリックします。または、アドレスバーに「chrome://extensions」と入力してアクセスできます。

右上にある「デベロッパーモード」のトグルスイッチをオンにして、デベロッパーモードを有効化します。

「パッケージ化されていない拡張機能を読み込む」ボタンをクリックし、エクステンションのルートディレクトリ(マニフェストファイルが含まれているディレクトリ)を選択します。これで、エクステンションがローカルで読み込まれます。

テストします。問題がないことを確認したら、次のステップに進みます。

エクステンションのパッケージング

拡張を機能をパッケージ化します。拡張機能のルートディレクトリの閲覧を押してパッケージ化したいフォルダを指定します。

エクステンションの公開

お客様の拡張機能は、ホスト権限が要求されているため、詳しい審査が必要となり、公開が遅れる可能性があります。

ホスト権限が、all_urlsになっているからでした。すべてのwebページが対象なので、これで審査を申請します。

"matches": [

"<all_urls>"

],

ChatGPTでチャットして作成してみて

おおまかな全体像は示してくれる

最初こんな感じでChatGPTに投げかけたところ

ChatGPTで自動要約するgoogle chrome extensionを作成して

全体的にいくつかのソースコードを書いてくれましたけど、動作しなかったです。

プログラムの知識が必要

ChatGPTにバグを伝えたら修正はしてくれるんですが、それでも動作しなかったです。

何度もそれを繰り返していたら泥沼感に陥りました。

ChatGPTに聞く側にやはりプログラムの知識がないと軌道修正しようがありませんでした。

目標に達成する効率の良い質問することが大事だなと思いました。

Google chrome エクステンションの概要とOpenAI のAPIを使用する方法を自分で調べて、結局、完成まで2日間かかりました。

メリットはめちゃくちゃある

なにがよかったかというと、作成したい意図を伝えるだけでコードをすらすら書いてくれるので、その手間が省けるのがよかったです。

普通に作成するよりは負担は減りましたし効率もすごいよかったです。

公開したものは今、審査中なので通った後公開します。