見出し画像

TwitterやnoteなどChatGPTを色々なサイトで呼び出して自動でコンテンツ作成。Google拡張を自作して英訳、Tweet、ブログ記事作成を自動化。作業効率大幅UP。


やりたいことできること

文章作成や翻訳などコマンドでChatGPTを呼び出しtwitterやnoteなど様々なサイトで使えるようにする。

blog: 〇〇についてのレビューを書いて++gpt
tweet: フォロワーに挨拶して++gpt
english: こんにちは++gpt
などコマンド+テキスト+任意の呼び出し文字で状況に応じたAIのアイスタント機能を色々なサイトで受けられるようにする。

デモ

ブログの文章を作成

tweetの作成

翻訳

ファイル構成

Your folder name/
    manifest.js
    scripts/
        content.js
        background.js
    icons/
        48.png
        72.png
        96.pne

manifest.jsを用意

まずmanifest.jsファイルを用意する。拡張機能についての情報や説明などを決められたフォーマットで書いたマニフェストファイル を作成。 マニフェストファイルは決められたファイル名 manifest.jsonで作る必要があります

{
  "name": "ContentGeneratorGPT",
  "description": "Generate content based on prompts with AI",
  "version": "1.0",
  "manifest_version": 3,
  "icons": {
    "48": "icons/48.png",
    "72": "icons/72.png",
    "96": "icons/96.png"
  },
  "action": {
    "default_title": "ContentGeneratorGPT"
  },
  "permissions": ["activeTab", "tabs", "storage"],
  "content_scripts": [
    {
      "matches": ["https://*/*", "http://*/*"],
      "js": ["scripts/content.js"]
    }
  ],
  "background": {
    "service_worker": "scripts/background.js"
  }
}

ChatGPTからの応答を挿入するcontent.jsファイルを作成

次に特定のコマンドを打ち込むと内容を入力をOpenAIに送信し、応答を挿入するコードを作成。

content.js

// contentを引数に取り、現在アクティブなHTMLエレメントに内容を挿入する関数
const insert = (content) => {
  var element = document.activeElement;

  // エレメントがテキストエリアまたはインプットフィールドの場合
  if (element.nodeName === "TEXTAREA" || element.nodeName === "INPUT") {
    element.value = content;
  } else { // それ以外のエレメントの場合
    element.innerText = content;
  }

  return true;
};

// 入力の終了を示す記号を定義
const endSign = "++gpt";

// 引数で与えられた文字列がendSignで終わっているかどうかを確認する関数
const isEndsWithSign = (str) => {
  return str.trim().endsWith(endSign);
};

// 引数で与えられたテキストをバックエンドに送信する関数
const sendTriggerMessage = (text) => {
  // メッセージとして"generate_ai"を、contentとしてtextを送信
  chrome.runtime.sendMessage({
    message: "generate_ai",
    content: text,
  });
};

// ユーザーが何かを入力した時にイベントが発火するようにリスナーを設定
document.addEventListener("input", (event) => {
  // ページが非アクティブになったときにイベントリスナーを削除
  if (document.visibilityState === "hidden") {
    document.removeEventListener("input", arguments.callee);
    return;
  }
  const target = event.target;
  var userInput = target.innerText || target.value;

  // ユーザー入力がendSignで終わっている場合
  if (isEndsWithSign(userInput)) {
    // endSignをトリムした後のユーザー入力をプロンプトとして取得
    const prompt = userInput.slice(0, -endSign.length).trim();
    // プロンプトをバックエンドに送信
    sendTriggerMessage(prompt);
  }
});

// バックエンドからのメッセージをリッスンするリスナーを設定
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // メッセージが"inject_ai"の場合、その内容をアクティブなエレメントに挿入
  if (request.message === "inject_ai") {
    insert(request.content);
  }
});

上記のコードでは++gptとテキストの後に打ち込むことで文章を自動で作成できるようにしています。
insert関数: 引数として受け取ったcontentをアクティブなHTMLエレメント(inputタグまたはtextareaタグ、それ以外の場合はinnerText)に挿入します。

  • endSign変数: ユーザーの入力が終了したことを示す記号を定義します。このコードでは++gptがその記号として使用されています。

  • isEndsWithSign関数: 引数として受け取った文字列がendSignで終了しているかどうかを確認します。

  • sendTriggerMessage関数: 引数として受け取ったテキストをバックエンドに送信します。ここでは、Chromeのメッセージ送信機能を使用して、"generate_ai"というメッセージと共にテキストを送信しています。

  • addEventListener関数: ドキュメント全体に"input"イベントのリスナーを追加します。これにより、ユーザーが何かを入力したときにイベントがトリガーされます。イベントがトリガーされると、ユーザーの入力がendSignで終了しているかどうかを確認し、その場合は入力をバックエンドに送信します。

  • onMessage.addListener関数: メッセージリスナーを追加します。これにより、バックエンドからのメッセージをリッスンし、メッセージが"inject_ai"の場合はその内容をアクティブなエレメントに挿入します。

OpenAIのAPIを呼び出してChatGPTによる応答を取得して結果を挿入するbackground.jsファイルを作成

OpenAIのAPIを呼び出してAIによる応答を生成し、その結果をアクティブなタブに挿入します。YOUR_AIP_KEYに取得したapi keyを適宜入れて下さい。

api keyはhttps://platform.openai.com/account/api-keysから取得

background.js

//OpenAIのApi key
const key = YOUR_AIP_KEY

const tmpStoreTab = (tab) => {
  // Save the tab's id and url to local storage
  chrome.storage.local.set({ tab: tab });
};

// 引数で与えられた内容を現在アクティブなタブに送信する関数
const sendResponseMessage = async (content) => {
  let tabs;
  try {
    tabs = await chrome.tabs.query({
      active: true,
      lastFocusedWindow: true,
    });

    if (tabs.length > 0) {
      tmpStoreTab(tabs[0]);
    }
  } catch (error) {
    console.error("Error querying tabs: ", error);
    return;
  }

  chrome.storage.local.get("tab", function (data) {
    if (chrome.runtime.lastError || !data.tab) {
      console.log("No stored tab found.");
      return;
    }

    let storedTab = data.tab;

    if (tabs.length === 0 && storedTab) {
      // If there are no active tabs, send the message to the stored tab
      chrome.tabs.sendMessage(storedTab.id, {
        message: "inject_ai",
        content,
      });
    } else if (tabs.length > 0) {
      // If there are active tabs, send the message to the first one
      chrome.tabs.sendMessage(tabs[0].id, {
        message: "inject_ai",
        content,
      });
    } else {
      console.log("No active tab found.");
      return;
    }
  });
};

// OpenAIのAPIを呼び出して、AIによる応答を生成する関数
const generateContent = async (prompt) => {
  

  // APIキーが存在しない場合はエラー
  if (!key) {
    console.log("API key is missing");
    return;
  }

  // プロンプトが空の場合は終了
  if (!prompt) return;
  console.log("Prompt: ", prompt);

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

  // completionsエンドポイントを呼び出す
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${key}`,
    },
    body: JSON.stringify({
      model: "gpt-3.5-turbo",
      messages: [
        { role: "system", content: "You are a helpful assistant." },
        { role: "user", content: prompt },
      ],
    }),
  });

  // 応答が不適切な場合はエラー
  if (!response.ok) {
    console.log(`HTTP error! status: ${response.status}`);
    return;
  }

  // OpenAIからの完了をJSONで取得
  const completion = await response.json();
  console.log("Completion: ", completion);

  // エラーメッセージを処理
  if (completion.error) {
    console.log("Error message from API: ", completion.error.message);
    return;
  }

  return completion.choices[0].message.content.trim();
};

// ユーザーの指示に基づいてプロンプトを生成する関数
const createPrompt = (command, text) => {
  switch (command) {
    case "tweet":
      return `Write me a tweet about ${text}`;
    case "blog":
      return `Write me a blog post about ${text}`;
    case "english":
      return `Translate the below text to English: ${text}`;
    case "python":
      return `Write a python code that ${text}. I do not want any explanations, notes or text reply other than code blocks at all. Please reponse in the format of code blocks only.`;
    default:
      return text;
  }
};

// プロンプトを生成し、そのプロンプトを用いてAIによる応答を生成し、その結果をアクティブなタブに挿入する関数
const generateAiResponse = async ({ command, text }) => {
  sendResponseMessage("generating...");

  try {
    // ステップ#1: ユーザーの指示に基づいて新しいプロンプトを作成
    const prompt = createPrompt(command, text);
    // ステップ#2: 新しいプロンプトで内容を生成
    const response = await generateContent(prompt);
    // 応答エラーを処理
    console.log("Response: ", response);
    if (!response) {
      console.log("API response does not contain expected data");
      return;
    }
    const result = response.slice(2);

    console.log("Generated content: ", result);
    sendResponseMessage(result);
  } catch (error) {
    console.log(error);
  }
};

// ユーザーからの指示を解析し、指示とテキストを抽出する関数
const getPrompt = (str) => {
  const index = str.indexOf(":");
  const command = str.slice(0, index);
  const text = str.slice(index + 1);

  return { command, text };
};

// バックエンドからのメッセージをリッスンし、メッセージが"generate_ai"の場合はその内容を解析してAIによる応答を生成するリスナーを設定
chrome.runtime.onMessage.addListener((request) => {
  if (request.message === "generate_ai") {
    const prompt = getPrompt(request.content);
    generateAiResponse(prompt);
  }
});
  • sendResponseMessage関数: 引数として受け取ったcontentを現在アクティブなタブに送信します。

  • generateContent関数: OpenAIのAPIを呼び出して、AIによる応答を生成します。

  • createPrompt関数: ユーザーからの指示に基づいて特定のプロンプトを生成します。

  • generateAiResponse関数: プロンプトを生成し、そのプロンプトを用いてAIによる応答を生成し、その結果をアクティブなタブに挿入します。

  • getPrompt関数: ユーザーからの指示を解析して、指示とテキストを抽出します。

  • chrome.runtime.onMessage.addListener関数: メッセージリスナーを追加します。これにより、バックエンドからのメッセージをリッスンし、メッセージが"generate_ai"の場合はその内容を解析してAIによる応答を生成します。

コマンドを拡張して機能を追加

background.jsファイルの以下の部分を変更するとできる作業を追加することができます。

const createPrompt = (command, text) => {
  switch (command) {
    case "tweet":
      return `Write me a tweet about ${text}`;
    case "blog":
      return `Write me a blog post about ${text}`;
    case "english":
      return `Translate the below text to English: ${text}`;
    case "python":
      return `Write a python code that ${text}. I do not want any explanations, notes or text reply other than code blocks at all. Please reponse in the format of code blocks only.`;
    default:
      return text;
  }
};

上記のコードにコマンドとその内容を追加すれば様々な作業を追加できます。例えば英語以外の訳に中国語訳をして欲しい場合は以下のようにコマンドを追加すると中国語訳を作成できるようになります。

const createPrompt = (command, text) => {
  switch (command) {
    case "tweet":
      return `Write me a tweet about ${text}`;
    case "blog":
      return `Write me a blog post about ${text}`;
    case "english":
      return `Translate the below text to English: ${text}`;
    //以下を追加
    case "chinese":
      return `Translate the below text to Chinese: ${text}`;
    case "python":
      return `Write a python code that ${text}. I do not want any explanations, notes or text reply other than code blocks at all. Please reponse in the format of code blocks only.`;
    default:
      return text;
  }
};

拡張機能の追加

chrome://extensions/
からパッケージ化されていない拡張機能を読み込むボタンを押してフォルダーを選び読み込む。後は拡張機能を追加すればアシスタントを使えるようになる。




この記事が気に入ったらサポートをしてみませんか?