はじめに
結論から言うと完璧を目指すならbrowser-useを使えって感じですが、
料金を抑えつつ、ある程度カジュアルに作るならこれでいい気がします。
前提
- Chrome拡張の作り方の説明はしない
- 拡張機能のインストール方法の説明はしない
各ファイル
全てディレクトリ直下に格納してます。
manifest.json
manifest.json
{
"manifest_version": 2,
"name": "AIフォーム自動入力",
"version": "1.0",
"description": "AI支援によるフォーム自動入力機能を持つChrome拡張機能",
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/icon16.png", // iconは適当に準備してください。
"48": "images/icon48.png",
"128": "images/icon128.png"
}
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"storage",
"tabs",
"activeTab",
"<all_urls>",
"https://api.openai.com/*"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_idle"
}
],
"web_accessible_resources": [
"content-script.js"
]
}
popup.html
ポップアップの見た目部分です。
popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AIフォーム自動入力</title>
<style>
body {
width: 350px;
padding: 10px;
font-family: Arial, sans-serif;
}
.form-group {
margin-bottom: 10px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, textarea, select {
width: 100%;
padding: 6px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
textarea {
min-height: 80px;
resize: vertical;
}
small {
color: #666;
display: block;
margin-top: 3px;
font-size: 0.8em;
}
button {
background-color: #4285F4;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
margin-right: 5px;
}
button:hover {
background-color: #3367D6;
}
.saved-message {
color: green;
margin-top: 10px;
display: none;
}
h2 {
margin-top: 0;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.container {
padding: 6px 12px;
}
.checkbox-group {
display: flex;
align-items: center;
margin-top: 5px;
}
.checkbox-group input[type="checkbox"] {
width: auto;
margin-right: 8px;
}
.checkbox-group label {
display: inline;
font-weight: normal;
}
</style>
</head>
<body>
<h2>AIフォーム自動入力</h2>
<div class="container">
<div class="form-group">
<label for="apiKey">OpenAI APIキー:</label>
<input type="password" id="apiKey" placeholder="sk-..." autocomplete="off">
<small>APIキーは安全に保存され、フォーム自動入力のために使用されます</small>
</div>
<div class="form-group">
<label for="model">AIモデル:</label>
<select id="model">
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
<option value="gpt-4">GPT-4</option>
</select>
</div>
<div class="form-group">
<label for="formContext">フォームコンテキスト情報:</label>
<textarea id="formContext" placeholder="フォームの目的や必要な入力情報について説明してください。例: 「これは求人応募フォームで、エンジニアのポジションに応募したいです」"></textarea>
<small>AIがフォームをより適切に理解し、最適な値を入力するための補助情報を入力します</small>
</div>
<button id="saveSettingsButton">設定を保存</button>
<button id="autoFillButton">フォームを自動入力</button>
<p id="settingsSavedMessage" class="saved-message">設定を保存しました!</p>
<p id="autoFillMessage" class="saved-message">自動入力を開始しました!</p>
</div>
<script src="popup.js"></script>
</body>
</html>
popup.js
入力やボタン押下時の制御周りの処理です。
popup.js
document.addEventListener('DOMContentLoaded', function() {
// DOM要素の取得
const apiKeyInput = document.getElementById('apiKey');
const modelSelect = document.getElementById('model');
const formContextInput = document.getElementById('formContext');
const saveSettingsButton = document.getElementById('saveSettingsButton');
const settingsSavedMessage = document.getElementById('settingsSavedMessage');
const autoFillButton = document.getElementById('autoFillButton');
const autoFillMessage = document.getElementById('autoFillMessage');
// 保存されたAPIキー設定を読み込む
chrome.storage.sync.get(['apiSettings'], function(result) {
if (result.apiSettings) {
apiKeyInput.value = result.apiSettings.apiKey || '';
modelSelect.value = result.apiSettings.model || 'gpt-3.5-turbo';
if (formContextInput) {
formContextInput.value = result.apiSettings.formContext || '';
}
}
});
// APIキー設定保存ボタンのクリックイベント
saveSettingsButton.addEventListener('click', function() {
// APIキー設定を取得
const apiSettings = {
apiKey: apiKeyInput.value,
model: modelSelect.value,
formContext: formContextInput ? formContextInput.value : ''
};
// データを保存
chrome.storage.sync.set({apiSettings: apiSettings}, function() {
// 保存完了メッセージを表示
settingsSavedMessage.style.display = 'block';
// 3秒後にメッセージを非表示にする
setTimeout(function() {
settingsSavedMessage.style.display = 'none';
}, 3000);
});
});
// フォーム自動入力ボタンのクリックイベント
autoFillButton.addEventListener('click', function() {
console.log('自動入力ボタンがクリックされました');
// APIキー設定を取得
chrome.storage.sync.get(['apiSettings'], function(result) {
console.log('APIキー設定を取得しました:', result.apiSettings ? 'あり' : 'なし');
if (!result.apiSettings || !result.apiSettings.apiKey) {
alert('OpenAI APIキーを設定してください');
return;
}
// 現在のタブにメッセージを送信
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if (!tabs || tabs.length === 0) {
console.error('アクティブなタブが見つかりません');
autoFillMessage.textContent = 'エラー: アクティブなタブが見つかりません';
autoFillMessage.style.color = 'red';
autoFillMessage.style.display = 'block';
return;
}
const activeTab = tabs[0];
console.log('アクティブなタブ:', activeTab.id);
// 代替アプローチ: 直接フォーム自動入力を実行する
autoFillMessage.textContent = 'フォーム分析中...';
autoFillMessage.style.color = 'blue';
autoFillMessage.style.display = 'block';
// まずcontent-scriptを強制的に挿入
chrome.tabs.executeScript(
activeTab.id,
{ file: 'content-script.js' },
function(injectionResults) {
if (chrome.runtime.lastError) {
console.error('スクリプト注入エラー:', chrome.runtime.lastError);
autoFillMessage.textContent = `エラー: ${chrome.runtime.lastError.message}`;
autoFillMessage.style.color = 'red';
autoFillMessage.style.display = 'block';
return;
}
console.log('content-scriptを注入または再読み込みしました');
// 少し待ってからメッセージを送信してみる
setTimeout(function() {
try {
chrome.tabs.sendMessage(
activeTab.id,
{
action: 'autoFillForms',
apiKey: result.apiSettings.apiKey,
model: result.apiSettings.model || 'gpt-3.5-turbo',
formContext: result.apiSettings.formContext || ''
},
function(response) {
console.log('content-scriptからのレスポンス:', response);
if (chrome.runtime.lastError) {
console.warn('メッセージ送信エラー (フォールバック実行を試みます):', chrome.runtime.lastError);
// フォールバック: 直接executeScriptで実行
executeDirectFill(activeTab.id, result.apiSettings, autoFillMessage);
return;
}
if (response && response.success) {
// 成功メッセージを表示
autoFillMessage.textContent = response.message;
autoFillMessage.style.color = 'green';
autoFillMessage.style.display = 'block';
// 3秒後にメッセージを非表示にする
setTimeout(function() {
autoFillMessage.style.display = 'none';
}, 3000);
} else {
// エラーメッセージを表示
autoFillMessage.textContent = response ? response.message : 'フォーム自動入力中にエラーが発生しました';
autoFillMessage.style.color = 'red';
autoFillMessage.style.display = 'block';
}
}
);
} catch (error) {
console.error('メッセージ送信エラー:', error);
// エラーの場合も直接実行を試みる
executeDirectFill(activeTab.id, result.apiSettings, autoFillMessage);
}
}, 500);
}
);
});
});
});
});
// 直接実行関数
function executeDirectFill(tabId, apiSettings, messageElement) {
console.log('直接実行を試みます', apiSettings);
// フォームコンテキストをエスケープして安全に扱う
const safeFormContext = apiSettings.formContext ?
apiSettings.formContext.replace(/'/g, "\\'").replace(/\n/g, "\\n") : '';
chrome.tabs.executeScript(
tabId,
{
code: `
// content-scriptの関数を直接呼び出す
if (typeof window.directExecute === 'function') {
console.log('directExecuteを実行します');
window.directExecute('autoFillForms', '${apiSettings.apiKey}', '${apiSettings.model || 'gpt-3.5-turbo'}', {
formContext: '${safeFormContext}'
});
} else if (typeof window.detectForms === 'function') {
console.log('content-scriptの関数は存在しますが、directExecuteがありません');
{ success: false, message: 'バージョンの不一致: 拡張機能を再読み込みしてください' };
} else {
console.error('content-scriptの関数が見つかりません');
{ success: false, message: 'content-scriptが正しく読み込まれていません' };
}
`
},
function(results) {
console.log('直接実行結果:', results);
if (chrome.runtime.lastError) {
console.error('直接実行エラー:', chrome.runtime.lastError);
messageElement.textContent = `エラー: ${chrome.runtime.lastError.message}`;
messageElement.style.color = 'red';
messageElement.style.display = 'block';
return;
}
// 直接実行では正確な結果が取得できないため、処理中メッセージを表示
messageElement.textContent = 'フォーム処理中...完了まで少々お待ちください';
messageElement.style.color = 'blue';
// 5秒後に完了メッセージを表示(非同期処理なので厳密な完了確認はできない)
setTimeout(function() {
messageElement.textContent = 'フォーム処理が完了しました(自動推測)';
messageElement.style.color = 'green';
// さらに3秒後に非表示
setTimeout(function() {
messageElement.style.display = 'none';
}, 3000);
}, 5000);
}
);
}
background.js
スクリプトの読み込みなどの準備
background.js
// Chrome拡張機能のインストール/更新時に実行
chrome.runtime.onInstalled.addListener(function() {
console.log('AI フォーム自動入力拡張機能がインストールされました');
});
// エラーを検出するリスナー
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.type === 'error') {
console.error('エラーが報告されました:', message.error);
}
if (message.type === 'debug') {
console.log('デバッグメッセージ:', message.data);
}
// sendResponseが使われる可能性があるので、trueを返す
return true;
});
// コンテンツスクリプトとの接続確認
chrome.runtime.onConnect.addListener(function(port) {
console.log('新しい接続:', port.name);
port.onMessage.addListener(function(message) {
console.log('ポートメッセージ:', message);
});
port.onDisconnect.addListener(function() {
console.log('ポート接続が終了しました:', port.name);
});
});
// コンテンツスクリプトが読み込まれたときのメッセージリスナー
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.action === 'contentScriptLoaded') {
console.log('Content Scriptが読み込まれました:', sender.tab ? sender.tab.url : 'unknown');
// 応答を返す
sendResponse({status: 'confirmed'});
}
// 非同期レスポンスのためにtrueを返す
return true;
});
content-script.js
アクティブなタブ上にあるフォームを検出して自動入力するコア機能
content-script.js
// フォーム要素を検出する関数
function detectForms() {
// まず標準的な<form>要素を探す
const forms = document.querySelectorAll('form');
if (forms.length > 0) {
console.log('フォームが検出されました:', forms.length, '件');
return forms;
}
// フォームが見つからない場合、入力フィールドの集まりを探す
// 入力フィールドを集めて仮想的なフォームを作成
console.log('標準フォームが見つからないため、入力フィールドを探します');
const inputElements = document.querySelectorAll('input, textarea, select');
if (inputElements.length > 0) {
console.log('入力フィールドが見つかりました:', inputElements.length, '件');
// 仮想フォーム(div要素)を作成
const virtualForm = document.createElement('div');
virtualForm.setAttribute('data-virtual-form', 'true');
// 入力要素の参照を保持(実際にDOM操作はしない)
virtualForm.inputElements = inputElements;
// 仮想フォーム用のquerySelectorAll関数をオーバーライド
virtualForm.querySelectorAll = function(selector) {
if (selector === 'input, textarea, select') {
return this.inputElements;
}
return [];
};
return [virtualForm];
}
console.log('フォームも入力フィールドも見つかりませんでした');
return [];
}
// OpenAIのAPIを使ってフォームフィールドを解析する関数
async function analyzeFormWithAI(formElement, apiKey, model, formContext = '') {
// フォーム内の入力要素を取得
const inputElements = formElement.querySelectorAll('input, textarea, select');
if (!inputElements || inputElements.length === 0) {
throw new Error('フォーム内に入力要素が見つかりません');
}
console.log('入力要素数:', inputElements.length);
const formFields = Array.from(inputElements).map(element => {
// 基本的なラベル検出
let label = findLabelForElement(element);
return {
id: element.id,
name: element.name,
type: element.type,
placeholder: element.placeholder,
label: label,
required: element.required
};
});
console.log('解析するフォームフィールド:', formFields);
// 入力可能な要素のみをフィルタリング
const editableFields = formFields.filter(field => {
const type = field.type.toLowerCase();
return type !== 'submit' && type !== 'button' && type !== 'reset' &&
type !== 'hidden' && type !== 'file' && type !== 'image';
});
if (editableFields.length === 0) {
throw new Error('フォーム内に入力可能な要素が見つかりません');
}
// OpenAI APIへのリクエスト
try {
console.log(`OpenAI API (${model})にリクエストを送信中...`);
const systemMessage = `
あなたはウェブフォームの自動入力を支援するAIアシスタントです。
以下のフォームフィールド情報に基づいて、各フィールドに適切な値を入力してください。
必ず、以下の形式でJSON形式で回答してください:
{
"フィールドID1": "入力値1",
"フィールドID2": "入力値2",
...
}
注意:
- フィールドIDがない場合は、nameまたはlabelを使用してください
- 実際の値のみを含め、説明やコメントは含めないでください
- 妥当な日本語の値を使用してください(例: 名前なら「山田太郎」など)
- メールアドレスには有効な形式を使用してください(例: user@example.com)
- パスワードには複雑なパスワードを使用してください
- checkboxやradioボタンには "true"/"false" または "1"/"0" で応答してください
`;
let userMessage = `
このウェブフォームに適切な値を入力してください。以下はフォームフィールドの詳細情報です:
${JSON.stringify(editableFields, null, 2)}
各フィールドに適切な値を割り当てたJSONオブジェクトで回答してください。
フィールドIDまたは名前をキーとして使用してください。
`;
// ユーザーが提供したコンテキスト情報があれば追加
if (formContext && formContext.trim() !== '') {
userMessage += `
フォームに関する追加コンテキスト情報:
${formContext}
この情報に基づいて、フォームフィールドに適切な値を入力してください。
`;
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: model,
messages: [
{
role: 'system',
content: systemMessage
},
{
role: 'user',
content: userMessage
}
],
temperature: 0.2, // より決定論的な結果を得るために低い値を設定
...(model.includes('gpt-4') ? {} : { response_format: { type: "json_object" } }) // JSON形式の応答を要求
})
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
console.error('API応答エラー:', response.status, errorData);
throw new Error(`APIエラー: ${response.status} - ${errorData?.error?.message || '不明なエラー'}`);
}
const data = await response.json();
console.log('AIレスポンス:', data);
if (data.choices && data.choices.length > 0) {
const contentResult = parseAIResponse(data.choices[0].message.content, editableFields);
if (Object.keys(contentResult).length === 0) {
console.warn('AIレスポンスのパースに失敗しました。レスポンス:', data.choices[0].message.content);
return null;
}
return contentResult;
} else {
throw new Error('AIからの有効なレスポンスがありません');
}
} catch (error) {
console.error('AI APIエラー:', error);
throw error; // 上位の関数でキャッチできるようにエラーを再スロー
}
}
// AIのレスポンスをパースする関数
function parseAIResponse(responseText, formFields) {
console.log('AIレスポンスのパース開始:', responseText.substring(0, 100) + '...');
// この関数はAIのレスポンスをパースして、各フォームフィールドの値を返す
const result = {};
try {
// ステップ1: JSONとして解析を試みる
let parsedJson = null;
if (responseText.includes('{') && responseText.includes('}')) {
try {
// JSON部分を抽出する試み
const jsonRegex = /{[\s\S]*?}/g;
const jsonMatches = responseText.match(jsonRegex);
if (jsonMatches && jsonMatches.length > 0) {
// 最も長いJSONを試す
const sortedMatches = [...jsonMatches].sort((a, b) => b.length - a.length);
for (const jsonString of sortedMatches) {
try {
parsedJson = JSON.parse(jsonString);
console.log('JSONとして解析成功:', parsedJson);
break;
} catch (e) {
console.log('JSONパース失敗、次の候補を試します');
}
}
}
} catch (jsonError) {
console.log('JSON抽出エラー:', jsonError);
}
}
// ステップ2: 解析したJSONから値を抽出
if (parsedJson) {
console.log('JSONからフィールド値を抽出します');
for (const field of formFields) {
const fieldId = field.id || field.name;
const fieldLabel = field.label ? field.label.toLowerCase() : '';
if (!fieldId && !fieldLabel) continue;
// プロパティを直接検索
if (fieldId && parsedJson[fieldId] !== undefined) {
result[fieldId] = parsedJson[fieldId];
continue;
}
// ラベルで検索
if (fieldLabel && parsedJson[fieldLabel] !== undefined) {
result[fieldId || fieldLabel] = parsedJson[fieldLabel];
continue;
}
// プロパティ名の一部一致での検索
for (const key in parsedJson) {
const keyLower = key.toLowerCase();
if (fieldId && keyLower.includes(fieldId.toLowerCase())) {
result[fieldId] = parsedJson[key];
break;
}
if (fieldLabel && keyLower.includes(fieldLabel)) {
result[fieldId || fieldLabel] = parsedJson[key];
break;
}
}
}
}
// ステップ3: 行ごとの解析(JSON解析が失敗した場合、または結果が不十分な場合)
if (Object.keys(result).length < formFields.length / 2) {
console.log('行ごとの解析を実行します');
const lines = responseText.split('\n');
for (const line of lines) {
if (line.includes(':')) {
// フォーマット例: "フィールド名: 値" または "フィールドID: 値"
const parts = line.split(':', 2);
if (parts.length === 2) {
const key = parts[0].trim().toLowerCase();
const value = parts[1].trim();
// 対応するフィールドを検索
for (const field of formFields) {
const fieldId = field.id || field.name;
const fieldLabel = field.label ? field.label.toLowerCase() : '';
if (!fieldId && !fieldLabel) continue;
// 完全一致
if ((fieldId && key === fieldId.toLowerCase()) ||
(fieldLabel && key === fieldLabel)) {
result[fieldId || fieldLabel] = value;
break;
}
// 部分一致
if ((fieldId && key.includes(fieldId.toLowerCase())) ||
(fieldLabel && key.includes(fieldLabel)) ||
(fieldId && fieldId.toLowerCase().includes(key)) ||
(fieldLabel && fieldLabel.includes(key))) {
result[fieldId || fieldLabel] = value;
break;
}
}
}
}
}
}
// ステップ4: プレースホルダーやフィールドタイプに基づく推測
if (Object.keys(result).length < formFields.length) {
console.log('デフォルト値の推測を実行します');
for (const field of formFields) {
const fieldId = field.id || field.name || field.label;
if (!fieldId || result[fieldId]) continue;
// 既に値が設定されていなければデフォルト値を設定
if (field.type === 'email') {
result[fieldId] = 'user@example.com';
} else if (field.type === 'password') {
result[fieldId] = 'SecurePassword123!';
} else if (field.type === 'tel') {
result[fieldId] = '090-1234-5678';
} else if (field.type === 'number') {
result[fieldId] = '123';
} else if (field.name && field.name.toLowerCase().includes('name')) {
result[fieldId] = '山田太郎';
} else if (field.placeholder) {
// プレースホルダーから推測
if (field.placeholder.toLowerCase().includes('email')) {
result[fieldId] = 'user@example.com';
} else if (field.placeholder.toLowerCase().includes('name')) {
result[fieldId] = '山田太郎';
}
}
}
}
console.log('パース結果:', result);
return result;
} catch (error) {
console.error('AIレスポンスのパースエラー:', error);
// エラーがあっても部分的な結果を返す
return result;
}
}
// 要素に関連するラベルを見つける関数
function findLabelForElement(element) {
// idがある場合、そのidを参照するlabelを探す
if (element.id) {
const label = document.querySelector(`label[for="${element.id}"]`);
if (label) {
return label.textContent.trim();
}
}
// 親要素内のラベルを探す
const parentElement = element.parentElement;
if (parentElement) {
const label = parentElement.querySelector('label');
if (label) {
return label.textContent.trim();
}
}
return '';
}
// フォームフィールドに値を入力する関数
function fillFormFields(form, fieldValues) {
const inputElements = form.querySelectorAll('input, textarea, select');
inputElements.forEach(element => {
const fieldId = element.id || element.name;
if (fieldId && fieldValues[fieldId]) {
if (element.type === 'checkbox' || element.type === 'radio') {
if (fieldValues[fieldId].toLowerCase() === 'true' ||
fieldValues[fieldId].toLowerCase() === 'yes' ||
fieldValues[fieldId] === '1') {
element.checked = true;
}
} else {
element.value = fieldValues[fieldId];
}
} else if (!fieldId && element.labels && element.labels.length > 0) {
// ラベルを使用して値を入力
const labelText = element.labels[0].textContent.trim();
if (labelText && fieldValues[labelText]) {
if (element.type === 'checkbox' || element.type === 'radio') {
if (fieldValues[labelText].toLowerCase() === 'true' ||
fieldValues[labelText].toLowerCase() === 'yes' ||
fieldValues[labelText] === '1') {
element.checked = true;
}
} else {
element.value = fieldValues[labelText];
}
}
}
});
}
// グローバルスコープで関数を定義(executeScriptから呼び出せるようにするため)
window.detectForms = detectForms;
// 直接実行用のグローバル関数
window.directExecute = function(action, apiKey, model, options = {}) {
console.log('directExecuteが呼び出されました:', action, options);
if (action === 'autoFillForms') {
// フォームコンテキスト情報
const formContext = options.formContext || '';
// 非同期処理を即時実行関数で実行
(async () => {
try {
// フォーム検出
console.log('フォーム検出を実行します');
const forms = detectForms();
if (forms.length === 0) {
console.error('フォームが見つかりませんでした');
return {
success: false,
message: 'ページ内にフォームまたは入力フィールドが見つかりませんでした'
};
}
// 最初のフォームに対して処理を実行
const fieldValues = await analyzeFormWithAI(forms[0], apiKey, model, formContext);
if (fieldValues && Object.keys(fieldValues).length > 0) {
fillFormFields(forms[0], fieldValues);
return {
success: true,
message: 'フォームを自動入力しました',
fields: Object.keys(fieldValues).length
};
} else {
return {
success: false,
message: 'フォームの解析に失敗しました'
};
}
} catch (error) {
console.error('フォーム処理エラー:', error);
return {
success: false,
message: `エラー: ${error.message}`
};
}
})();
return { status: 'processing' };
}
return { success: false, message: '不明なアクション' };
};
// メイン処理
console.log('content-script が読み込まれました');
// バックグラウンドスクリプトに読み込みを通知
try {
chrome.runtime.sendMessage({action: 'contentScriptLoaded'}, function(response) {
console.log('バックグラウンドスクリプトからの応答:', response);
});
} catch (err) {
console.error('バックグラウンドスクリプトへの通知エラー:', err);
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('メッセージを受信しました:', message);
// バックグラウンドスクリプトにデバッグメッセージを送信
try {
chrome.runtime.sendMessage({
type: 'debug',
data: { message: 'content-scriptがメッセージを受信', details: message }
});
} catch (err) {
console.error('デバッグメッセージ送信エラー:', err);
}
if (message.action === 'autoFillForms') {
const { apiKey, model, formContext } = message;
if (!apiKey) {
console.error('APIキーがありません');
sendResponse({ success: false, message: 'APIキーが設定されていません' });
return true;
}
// 非同期処理を開始
(async () => {
try {
// フォーム検出
console.log('フォーム検出を実行します');
const forms = detectForms();
if (forms.length === 0) {
console.error('フォームが見つかりませんでした');
sendResponse({
success: false,
message: 'ページ内にフォームまたは入力フィールドが見つかりませんでした'
});
return;
}
console.log('フォーム解析を開始します');
// 検出された最初のフォームを処理
const fieldValues = await analyzeFormWithAI(
forms[0],
apiKey,
model,
formContext
);
console.log('解析結果:', fieldValues);
if (fieldValues && Object.keys(fieldValues).length > 0) {
fillFormFields(forms[0], fieldValues);
sendResponse({
success: true,
message: 'フォームを自動入力しました',
fields: Object.keys(fieldValues).length
});
} else {
sendResponse({
success: false,
message: 'フォームの解析に失敗しました。入力フィールドに適切な値を見つけられませんでした。'
});
}
} catch (error) {
console.error('フォーム処理エラー:', error);
// エラーメッセージの詳細化
let errorMsg = error.message;
if (errorMsg.includes('API')) {
errorMsg = `OpenAI API エラー: ${errorMsg}`;
}
sendResponse({
success: false,
message: `エラー: ${errorMsg}`
});
// バックグラウンドスクリプトにエラーを報告
try {
chrome.runtime.sendMessage({
type: 'error',
error: {
message: errorMsg,
stack: error.stack
}
});
} catch (e) {
console.error('エラー報告中のエラー:', e);
}
}
})();
return true; // 非同期レスポンスのために true を返す
}
// デフォルトのレスポンス
sendResponse({ success: false, message: '不明なアクション' });
return true;
});
実際に動かしてみる
こちらのサイトにフォームの事例が載っているので、入力できるか試してみましょう。
下記がちょうど良さそうなのでやってみます。
こんな感じの設定にしました。
少し待つとこんな感じで入力できました。
生年月日も適当に入ってくれているのでそこそこ良さそうです