インターネットやめた
以下の仕様を満たすChrome extentionをつくりたいので手伝ってください
言語はTypeScriptとReact、できればDenoで作りたいです
アプリの仕様:
Twitter.comで利用するアプリ
ツイートの個別ページを開いた時、リプライが攻撃的ならそれをChatGPTで変換した文字列に置換します
はい、手伝います。以下はあなたが作成したいChrome拡張機能の仕様に基づいた、拡張機能の概要です。
拡張機能を作成するために必要な環境の準備
まず、TypeScript、React、およびDenoを使用してChrome拡張機能を作成するには、以下の手順に従って開発環境をセットアップする必要があります。
Node.jsおよびnpmをインストールします。
TypeScriptをインストールします。
Reactをインストールします。
Denoをインストールします。
Chrome拡張機能の開発に必要なnpmパッケージをインストールします。
拡張機能の構造と機能
拡張機能は、Chrome拡張機能の構造に従って作成する必要があります。拡張機能は、次の機能を提供する必要があります。
Twitter.comで個別ツイートのページを開いた時に、リプライが攻撃的であればChatGPTで変換した文字列に置換する。
ツイートのページを開くたびに、拡張機能が自動的に実行されるようにする。
拡張機能アイコンをクリックすると、拡張機能のオプションを表示することができる。
拡張機能の実装
拡張機能のエントリーポイントを作成します。拡張機能は、backgroundスクリプトとcontentスクリプトの両方を使用する必要があります。これにより、個別ツイートのページが開かれたときに、コンテンツスクリプトが実行され、攻撃的なリプライをChatGPTで変換できます。
ツイートのページが開かれたときに、コンテンツスクリプトが実行されるようにします。これは、backgroundスクリプトで実行することができます。
コンテンツスクリプトで、ページのDOMを操作して、攻撃的なリプライをChatGPTで変換するようにします。これには、ChatGPTにアクセスするために、Denoを使用することができます。
拡張機能アイコンをクリックしたときに、オプションページを表示するようにします。これには、Reactを使用してオプションページを作成する必要があります。オプションページでは、ユーザーがChatGPTの動作をカスタマイズできるようにすることができます。
最後に、拡張機能をデプロイするために、Chromeウェブストアに登録する必要があります。拡張機能のパッケージングとデプロイには、Chromeウェブストア用のマニフェストファイルを作成する必要があります。
実は一回作ったことがあるので流れは知っている
開発環境の構築がゆるふわすぎるのでGPT-4に聞いてみよう
以下の手順で、指定された仕様を満たすChrome Extensionを作成する方法を説明します。Denoはブラウザ環境で動作しないため、代わりにNode.jsとwebpackを使用して環境を構築します。
環境のセットアップ
まず、Node.jsとnpmがインストールされていることを確認してください。次に、新しいディレクトリを作成し、その中で以下のコマンドを実行します。
$ npm init
これにより、package.jsonファイルが生成されます。
必要なパッケージのインストール
以下のコマンドで、必要なパッケージをインストールします。
$ npm install --save-dev typescript ts-loader webpack webpack-cli react react-dom @types/react @types/react-dom @types/chrome
webpackの設定
プロジェクトのルートにwebpack.config.jsファイルを作成し、以下の内容を記述します。
TypeScriptの設定
プロジェクトのルートにtsconfig.jsonファイルを作成し、以下の内容を記述します。
Chrome Extensionのマニフェストファイルの作成
プロジェクトのルートにmanifest.jsonファイルを作成し、以下の内容を記述します。
Reactコンポーネントの作成
srcディレクトリを作成し、その中にindex.tsxファイルを作成します。次に、以下の内容を記述します。
index.tsximport * as React from "react";
import * as ReactDOM from "react-dom";
class App extends React.Component {
render() {
return <div>Hello, world!</div>;
}
}
ReactDOM.render(<App />, document.getElementById("app"));
ツイートの個別ページでリプライを監視する
srcディレクトリにtwitterObserver.tsファイルを作成し、以下の内容を記述します。
twitterObserver.tsconst TWEET_CONTAINER_SELECTOR = 'div[data-testid="primaryColumn"]';
const REPLIES_CONTAINER_SELECTOR = 'div[data-testid="reply"]';
export function observeTweets(callback: (element: HTMLElement) => void): void {
const tweetContainer = document.querySelector<HTMLElement>(TWEET_CONTAINER_SELECTOR);
if (tweetContainer) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLElement) {
const replyContainer = node.querySelector<HTMLElement>(REPLIES_CONTAINER_SELECTOR);
if (replyContainer) {
callback(replyContainer);
}
}
});
}
});
});
observer.observe(tweetContainer, {
childList: true,
subtree: true,
});
}
}
ChatGPT APIの呼び出し
srcディレクトリにchatGptApi.tsファイルを作成し、以下の内容を記述します。APIキーとエンドポイントを設定してください。
chatGptApi.tsexport async function generateFriendlyReply(inputText: string): Promise<string> {
const API_KEY = "your-api-key";
const API_ENDPOINT = "https://api.openai.com/v1/engines/davinci-codex/completions";
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
};
const prompt = `Translate the following aggressive reply to a friendly one: "${inputText}"`;
攻撃的かどうかはわからないから全てのツイートを入れることにした方がいい
攻撃的だったらfriendlyにしてと書き換えればよさそう
chatGptApi.ts const response = await fetch(API_ENDPOINT, {
method: "POST",
headers: headers,
body: JSON.stringify({
prompt: prompt,
max_tokens: 50,
n: 1,
stop: null,
temperature: 0.5,
}),
});
const data = await response.json();
return data.choices[0].text.trim();
}
リプライをChatGPTで変換し、置換する
src/index.tsxファイルを編集して、以下の内容を追加します。
index.tsximport { observeTweets } from "./twitterObserver";
import { generateFriendlyReply } from "./chatGptApi";
function modifyReplies(element: HTMLElement): void {
const aggressiveReplies = ["bad example reply 1", "bad example reply 2"]; // これらを攻撃的なリプライの判定方法に置き換えます
aggressiveReplies.forEach(async (reply) => {
const friendlyReply = await generateFriendlyReply(reply);
element.innerHTML = element.innerHTML.replace(reply, friendlyReply);
});
}
observeTweets(modifyReplies);
ビルドとデプロイ
以下のコマンドを実行して、プロジェクトをビルドします。
$ npx webpack
これにより、distディレクトリにbundle.jsファイルが生成されます...
コードは省略したけどかなり具体的だ。このタスクはGPT-4の方がいいな
Denoは使えないと言割れたけどテンプレートがあるからこれを使おう
なくて困るやつ
watch
aggressiveRepliesはツイートの個別ページについたリプライを取得するように変更してください
明らかに定数なところがあるからそこから変えていこう
src/index.tsxファイルを編集して、modifyReplies関数内でツイートの個別ページについたリプライを取得するように変更します。リプライのテキストを取得し、aggressiveReplies配列に格納する方法を以下のように変更してください。
index.tsximport { observeTweets } from "./twitterObserver";
import { generateFriendlyReply } from "./chatGptApi";
function modifyReplies(element: HTMLElement): void {
const replyElements = element.querySelectorAll<HTMLElement>('div[data-testid="tweet"] > div > div > div > div > div > div[dir="auto"] > span');
const aggressiveReplies: string[] = [];
replyElements.forEach((replyElement) => {
const replyText = replyElement.textContent;
// ここで攻撃的なリプライかどうかを判断するロジックを追加し、条件に一致する場合、aggressiveRepliesに追加します
if (isAggressive(replyText)) {
aggressiveReplies.push(replyText);
}
});
aggressiveReplies.forEach(async (reply) => {
const friendlyReply = await generateFriendlyReply(reply);
element.innerHTML = element.innerHTML.replace(reply, friendlyReply);
});
}
function isAggressive(replyText: string): boolean {
// ここで攻撃的なリプライかどうかを判断するロジックを実装します
// 例えば、キーワードリストや感情分析APIを使用して判断することができます
// この例では、簡単なキーワードリストを使用しています
const aggressiveKeywords = ["bad", "hate", "idiot", "stupid"];
return aggressiveKeywords.some((keyword) => replyText.includes(keyword));
}
observeTweets(modifyReplies);
この変更により、ツイートの個別ページについたリプライが取得され、isAggressive関数で攻撃的なリプライかどうかを判断し、攻撃的と判断されたリプライがaggressiveReplies配列に格納されます。そして、aggressiveReplies内のリプライがChatGPTで変換され、置換されます。
攻撃的なリプライを判断するロジックは、この例では簡単なキーワードリストを使用していますが、より高度な方法(例えば感情分析APIなど)を使用して実装することもできます。
やってみたがツイートが取得できなかったので2回やりとりするがそもそも構造が違うので取れない(そうだろうね)
これでも取得できませんでした。reactが使われているようですがtextを取得する方法はありますか?
例えば Object.keys(span).find(key => key.startsWith("__reactFiber"));
みたいにするとか
tsfunction getReactText(element: HTMLElement): string {
const reactKey = Object.keys(element).find((key) => key.startsWith("__reactFiber"));
if (reactKey && element[reactKey]) {
const children = element[reactKey].child.sibling.child.memoizedProps.children;
return children.map((child: { text: string }) => child.text).join("");
}
return "";
}
これもダメ
これでは取れませんでした。私が文法解析を手伝うのでreactのfiberの読み方を教えてください。fiberのhtmlをchrome dev toolsで見ましたが、階層がネストしすぎていて訳がわかりませんでした。読み方のポイントは?
modified by
ReactのFiberに関しては、確かにネストが深くて解析が難しいことがあります。
FiberはReactの内部実装であり、通常は直接アクセスしないようになっています。
Fiberの構造はReactのバージョンや内部実装によって変わる
Fiberツリーの主な概念は以下のとおりです。
Fiber: Reactのコンポーネントを表すオブジェクト。各Fiberはコンポーネントのインスタンスを表すため、状態やライフサイクルメソッドが関連付けられます。
Fiberツリー: これはコンポーネントの階層構造を表します。Fiberツリーは各コンポーネントの親子関係によって構築され、Reactアプリケーションの構造全体をカプセル化します。
Fiberオブジェクトには、以下のような主要なプロパティが含まれています。
child: 最初の子Fiberを指します。子が複数ある場合は、最初の子のみを指します。
sibling: 同じ階層の次の兄弟Fiberを指します。これは子同士の連結リストを形成します。
return: 親Fiberを指します。これは、Fiberツリーの上位に移動するために使用されます。
Fiberツリーの読み方は、以下の手順に従います。
最上位のFiberオブジェクト(通常は<App />コンポーネント)を見つけます。
childプロパティを使って子Fiberに移動します。
各子Fiberに対して、siblingプロパティを使って同じ階層の兄弟Fiberを調べます。全ての兄弟をチェックするまで続けます。
各子Fiberで、さらにchildプロパティを使って子Fiberに移動し、これらの子と兄弟に対しても同様の処理を繰り返します。
この方法で、Fiberツリー全体を探索できます。ただし、Fiberの構造はReactの内部実装に依存しており、Reactのバージョンや内部実装