Major refactor: Multi-user Chrome MCP extension with remote server architecture
This commit is contained in:
560
app/chrome-extension/inject-scripts/enhanced-search-helper.js
Normal file
560
app/chrome-extension/inject-scripts/enhanced-search-helper.js
Normal file
@@ -0,0 +1,560 @@
|
||||
/* eslint-disable */
|
||||
// enhanced-search-helper.js
|
||||
// Enhanced search automation with multiple submission methods
|
||||
|
||||
if (window.__ENHANCED_SEARCH_HELPER_INITIALIZED__) {
|
||||
// Already initialized, skip
|
||||
} else {
|
||||
window.__ENHANCED_SEARCH_HELPER_INITIALIZED__ = true;
|
||||
|
||||
/**
|
||||
* Perform Google search with enhanced reliability
|
||||
* @param {string} selector - CSS selector for the search box
|
||||
* @param {string} query - Search query
|
||||
* @returns {Promise<Object>} - Result of the search operation
|
||||
*/
|
||||
async function performGoogleSearch(selector, query) {
|
||||
try {
|
||||
console.log(`🔍 Attempting Google search with selector: ${selector}, query: ${query}`);
|
||||
|
||||
// Find the search element
|
||||
const searchElement = document.querySelector(selector);
|
||||
if (!searchElement) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Search element with selector "${selector}" not found`,
|
||||
};
|
||||
}
|
||||
|
||||
// Focus and clear the search box
|
||||
searchElement.focus();
|
||||
await sleep(200);
|
||||
|
||||
// Clear existing content
|
||||
searchElement.select();
|
||||
await sleep(100);
|
||||
|
||||
// Fill the search box
|
||||
searchElement.value = query;
|
||||
|
||||
// Trigger input events to ensure the page recognizes the input
|
||||
searchElement.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
searchElement.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
await sleep(500);
|
||||
|
||||
// Try multiple submission methods
|
||||
const submissionSuccess = await submitGoogleSearch(searchElement, query);
|
||||
|
||||
if (submissionSuccess) {
|
||||
console.log(`✅ Google search submitted successfully using selector: ${selector}`);
|
||||
return {
|
||||
success: true,
|
||||
selector,
|
||||
query,
|
||||
method: submissionSuccess.method,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: 'All submission methods failed',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in performGoogleSearch:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try multiple methods to submit Google search
|
||||
* @param {Element} searchElement - The search input element
|
||||
* @param {string} query - Search query
|
||||
* @returns {Promise<Object|null>} - Success result or null
|
||||
*/
|
||||
async function submitGoogleSearch(searchElement, query) {
|
||||
const methods = [
|
||||
{
|
||||
name: 'enter_key',
|
||||
action: async () => {
|
||||
console.log('🔄 Method 1: Trying Enter key');
|
||||
searchElement.focus();
|
||||
await sleep(200);
|
||||
|
||||
const enterEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
searchElement.dispatchEvent(enterEvent);
|
||||
await sleep(1000);
|
||||
|
||||
// Check if search was successful
|
||||
if (await checkSearchResultsLoaded()) {
|
||||
return { method: 'enter_key' };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'search_button',
|
||||
action: async () => {
|
||||
console.log('🔄 Method 2: Trying search button');
|
||||
|
||||
const buttonSelectors = [
|
||||
'input[value*="Google Search"]',
|
||||
'button[aria-label*="Google Search"]',
|
||||
'input[type="submit"][value*="Google Search"]',
|
||||
'.gNO89b', // Google Search button class
|
||||
'center input[type="submit"]:first-of-type',
|
||||
'button[type="submit"]',
|
||||
'[role="button"][aria-label*="search"]',
|
||||
'.Tg7LZd',
|
||||
];
|
||||
|
||||
for (const buttonSelector of buttonSelectors) {
|
||||
try {
|
||||
const button = document.querySelector(buttonSelector);
|
||||
if (button) {
|
||||
button.click();
|
||||
await sleep(1000);
|
||||
|
||||
if (await checkSearchResultsLoaded()) {
|
||||
return { method: 'search_button', selector: buttonSelector };
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'form_submit',
|
||||
action: async () => {
|
||||
console.log('🔄 Method 3: Trying form submission');
|
||||
|
||||
const form = searchElement.closest('form');
|
||||
if (form) {
|
||||
form.submit();
|
||||
await sleep(1000);
|
||||
|
||||
if (await checkSearchResultsLoaded()) {
|
||||
return { method: 'form_submit' };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'double_enter',
|
||||
action: async () => {
|
||||
console.log('🔄 Method 4: Trying double Enter');
|
||||
searchElement.focus();
|
||||
await sleep(200);
|
||||
|
||||
// First Enter
|
||||
const enterEvent1 = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
searchElement.dispatchEvent(enterEvent1);
|
||||
await sleep(300);
|
||||
|
||||
// Second Enter
|
||||
const enterEvent2 = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
searchElement.dispatchEvent(enterEvent2);
|
||||
await sleep(1000);
|
||||
|
||||
if (await checkSearchResultsLoaded()) {
|
||||
return { method: 'double_enter' };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const method of methods) {
|
||||
try {
|
||||
const result = await method.action();
|
||||
if (result) {
|
||||
console.log(`✅ Submission method "${method.name}" successful`);
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug(`Submission method "${method.name}" failed:`, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn('❌ All submission methods failed');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Google search results have loaded
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function checkSearchResultsLoaded() {
|
||||
const resultIndicators = [
|
||||
'#search', // Main search results container
|
||||
'#rso', // Results container
|
||||
'.g', // Individual result
|
||||
'.tF2Cxc', // Modern Google result container
|
||||
'#result-stats', // Search statistics
|
||||
'.yuRUbf', // Result link container
|
||||
];
|
||||
|
||||
for (const indicator of resultIndicators) {
|
||||
const element = document.querySelector(indicator);
|
||||
if (element && element.children.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract search results from the current page with intelligent selector discovery
|
||||
* @param {number} maxResults - Maximum number of results to extract
|
||||
* @returns {Promise<Object>} - Extracted results
|
||||
*/
|
||||
async function extractSearchResults(maxResults = 10) {
|
||||
try {
|
||||
console.log('🔍 Starting intelligent search result extraction...');
|
||||
const results = [];
|
||||
|
||||
// Try multiple selectors for Google search results
|
||||
const resultSelectors = [
|
||||
'.tF2Cxc', // Current Google search result container
|
||||
'.g', // Traditional Google search result
|
||||
'#rso .g', // Results container with .g class
|
||||
'.yuRUbf', // Google result link container
|
||||
'.rc', // Another Google result class
|
||||
];
|
||||
|
||||
let resultElements = [];
|
||||
let successfulSelector = null;
|
||||
|
||||
// First try standard selectors
|
||||
for (const selector of resultSelectors) {
|
||||
resultElements = document.querySelectorAll(selector);
|
||||
if (resultElements.length > 0) {
|
||||
successfulSelector = selector;
|
||||
console.log(`✅ Found results with standard selector: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If standard selectors fail, try intelligent discovery
|
||||
if (resultElements.length === 0) {
|
||||
console.log('🧠 Standard selectors failed, trying intelligent discovery...');
|
||||
const discoveryResult = await discoverSearchResultElements();
|
||||
resultElements = discoveryResult.elements;
|
||||
successfulSelector = discoveryResult.selector;
|
||||
}
|
||||
|
||||
// Extract results from found elements
|
||||
for (let i = 0; i < Math.min(resultElements.length, maxResults); i++) {
|
||||
const element = resultElements[i];
|
||||
|
||||
try {
|
||||
const extractedResult = extractResultFromElement(element, i + 1);
|
||||
if (extractedResult) {
|
||||
results.push(extractedResult);
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug(`Error extracting result ${i}:`, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
results,
|
||||
totalFound: results.length,
|
||||
selectorUsed: successfulSelector,
|
||||
method: resultElements.length > 0 ? 'extraction' : 'none',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error extracting search results:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
results: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intelligent discovery of search result elements
|
||||
* @returns {Object} - Object with elements array and successful selector
|
||||
*/
|
||||
async function discoverSearchResultElements() {
|
||||
console.log('🔬 Starting intelligent element discovery...');
|
||||
|
||||
// Intelligent selectors based on common patterns
|
||||
const intelligentSelectors = [
|
||||
// Modern Google patterns (2024+)
|
||||
'[data-ved] h3',
|
||||
'[data-ved]:has(h3)',
|
||||
'[data-ved]:has(a[href*="http"])',
|
||||
'[jscontroller]:has(h3)',
|
||||
'[jscontroller]:has(a[href*="http"])',
|
||||
|
||||
// Generic search result patterns
|
||||
'div[class*="result"]:has(h3)',
|
||||
'div[class*="search"]:has(h3)',
|
||||
'article:has(h3)',
|
||||
'li[class*="result"]:has(h3)',
|
||||
'[role="main"] div:has(h3)',
|
||||
|
||||
// Link-based patterns
|
||||
'a[href*="http"]:has(h3)',
|
||||
'div:has(h3):has(a[href*="http"])',
|
||||
|
||||
// Container patterns
|
||||
'div[class*="container"] > div:has(h3)',
|
||||
'div[id*="result"]:has(h3)',
|
||||
'div[id*="search"]:has(h3)',
|
||||
|
||||
// Semantic patterns
|
||||
'[role="article"]:has(h3)',
|
||||
'[role="listitem"]:has(h3)',
|
||||
'div[aria-label*="result"]:has(h3)',
|
||||
|
||||
// Fallback broad patterns
|
||||
'main div:has(h3)',
|
||||
'#main div:has(h3)',
|
||||
'.main div:has(h3)',
|
||||
'h3:has(+ div)',
|
||||
'div:has(h3)',
|
||||
];
|
||||
|
||||
for (const selector of intelligentSelectors) {
|
||||
try {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
if (elements.length > 0) {
|
||||
// Validate that these look like search results
|
||||
const validElements = Array.from(elements).filter((el) =>
|
||||
validateSearchResultElement(el),
|
||||
);
|
||||
|
||||
if (validElements.length > 0) {
|
||||
console.log(
|
||||
`✅ Found ${validElements.length} results with intelligent selector: ${selector}`,
|
||||
);
|
||||
return {
|
||||
elements: validElements,
|
||||
selector: `intelligent-${selector}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug(`Intelligent selector failed: ${selector}`, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback - DOM structure analysis
|
||||
console.log('🔬 Trying DOM structure analysis...');
|
||||
return analyzeDOMForSearchResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that an element looks like a search result
|
||||
* @param {Element} element - Element to validate
|
||||
* @returns {boolean} - True if element looks like a search result
|
||||
*/
|
||||
function validateSearchResultElement(element) {
|
||||
try {
|
||||
// Check for common search result indicators
|
||||
const hasHeading = element.querySelector('h1, h2, h3, h4, h5, h6');
|
||||
const hasLink = element.querySelector('a[href*="http"]');
|
||||
const hasText = element.textContent && element.textContent.trim().length > 50;
|
||||
|
||||
// Must have at least heading and link, or substantial text
|
||||
return (hasHeading && hasLink) || hasText;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze DOM structure to find search results using heuristics
|
||||
* @returns {Object} - Object with elements array and successful selector
|
||||
*/
|
||||
function analyzeDOMForSearchResults() {
|
||||
console.log('🔬 Analyzing DOM structure for search results...');
|
||||
|
||||
try {
|
||||
// Look for containers with multiple links (likely search results)
|
||||
const heuristicSelectors = [
|
||||
'div:has(a[href*="http"]):has(h3)',
|
||||
'li:has(a[href*="http"]):has(h3)',
|
||||
'article:has(a[href*="http"])',
|
||||
'main > div:has(h3)',
|
||||
'#main > div:has(h3)',
|
||||
'[role="main"] > div:has(h3)',
|
||||
'div:has(h3):has(a[href*="http"])',
|
||||
'section:has(h3):has(a[href*="http"])',
|
||||
];
|
||||
|
||||
for (const selector of heuristicSelectors) {
|
||||
try {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
if (elements.length > 0) {
|
||||
const validElements = Array.from(elements).filter((el) =>
|
||||
validateSearchResultElement(el),
|
||||
);
|
||||
|
||||
if (validElements.length > 0) {
|
||||
console.log(
|
||||
`✅ Found ${validElements.length} results with DOM analysis: ${selector}`,
|
||||
);
|
||||
return {
|
||||
elements: validElements,
|
||||
selector: `dom-analysis-${selector}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug(`DOM analysis selector failed: ${selector}`, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Ultimate fallback - any elements with links
|
||||
const fallbackElements = document.querySelectorAll('a[href*="http"]');
|
||||
if (fallbackElements.length > 0) {
|
||||
console.log(`⚠️ Using fallback: found ${fallbackElements.length} link elements`);
|
||||
return {
|
||||
elements: Array.from(fallbackElements).slice(0, 10), // Limit to 10
|
||||
selector: 'fallback-links',
|
||||
};
|
||||
}
|
||||
|
||||
console.warn('❌ DOM analysis failed to find any search results');
|
||||
return {
|
||||
elements: [],
|
||||
selector: null,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Error in DOM analysis:', e);
|
||||
return {
|
||||
elements: [],
|
||||
selector: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract result data from a single element
|
||||
* @param {Element} element - Element to extract from
|
||||
* @param {number} index - Result index
|
||||
* @returns {Object|null} - Extracted result or null
|
||||
*/
|
||||
function extractResultFromElement(element, index) {
|
||||
try {
|
||||
// Try multiple patterns for title extraction
|
||||
const titleSelectors = ['h3', 'h2', 'h1', '.LC20lb', '.DKV0Md', 'a[href*="http"]'];
|
||||
let titleElement = null;
|
||||
|
||||
for (const selector of titleSelectors) {
|
||||
titleElement = element.querySelector(selector);
|
||||
if (titleElement) break;
|
||||
}
|
||||
|
||||
// Try multiple patterns for link extraction
|
||||
const linkElement =
|
||||
element.querySelector('a[href*="http"]') || (element.tagName === 'A' ? element : null);
|
||||
|
||||
// Try multiple patterns for snippet extraction
|
||||
const snippetSelectors = ['.VwiC3b', '.s', '.st', 'p', 'div:not(:has(h1,h2,h3,h4,h5,h6))'];
|
||||
let snippetElement = null;
|
||||
|
||||
for (const selector of snippetSelectors) {
|
||||
snippetElement = element.querySelector(selector);
|
||||
if (snippetElement && snippetElement.textContent.trim().length > 20) break;
|
||||
}
|
||||
|
||||
// Extract data
|
||||
const title = titleElement?.textContent?.trim() || 'No title found';
|
||||
const url = linkElement?.href || '';
|
||||
const snippet = snippetElement?.textContent?.trim() || '';
|
||||
|
||||
// Validate we have meaningful data
|
||||
if (title && title !== 'No title found' && url) {
|
||||
return {
|
||||
title,
|
||||
url,
|
||||
snippet,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
console.debug(`Error extracting from element:`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep utility function
|
||||
* @param {number} ms - Milliseconds to sleep
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Listen for messages from the extension
|
||||
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
|
||||
if (request.action === 'performGoogleSearch') {
|
||||
performGoogleSearch(request.selector, request.query)
|
||||
.then(sendResponse)
|
||||
.catch((error) => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
});
|
||||
});
|
||||
return true; // Indicates async response
|
||||
} else if (request.action === 'extractSearchResults') {
|
||||
extractSearchResults(request.maxResults)
|
||||
.then(sendResponse)
|
||||
.catch((error) => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
results: [],
|
||||
});
|
||||
});
|
||||
return true; // Indicates async response
|
||||
} else if (request.action === 'enhanced_search_ping') {
|
||||
sendResponse({ status: 'pong' });
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
277
app/chrome-extension/inject-scripts/form-submit-helper.js
Normal file
277
app/chrome-extension/inject-scripts/form-submit-helper.js
Normal file
@@ -0,0 +1,277 @@
|
||||
/* eslint-disable */
|
||||
// form-submit-helper.js
|
||||
// Enhanced form submission with multiple methods
|
||||
|
||||
if (window.__FORM_SUBMIT_HELPER_INITIALIZED__) {
|
||||
// Already initialized, skip
|
||||
} else {
|
||||
window.__FORM_SUBMIT_HELPER_INITIALIZED__ = true;
|
||||
|
||||
/**
|
||||
* Submit a form using multiple methods
|
||||
* @param {string} formSelector - CSS selector for the form
|
||||
* @param {string} inputSelector - CSS selector for input field to focus (optional)
|
||||
* @param {string} submitMethod - Preferred submission method
|
||||
* @returns {Promise<Object>} - Result of the submission
|
||||
*/
|
||||
async function submitForm(formSelector = 'form', inputSelector = null, submitMethod = 'auto') {
|
||||
try {
|
||||
console.log(`🔄 Attempting form submission with method: ${submitMethod}`);
|
||||
|
||||
// Find the form
|
||||
let form = null;
|
||||
if (formSelector) {
|
||||
form = document.querySelector(formSelector);
|
||||
}
|
||||
|
||||
// If no specific form found, try to find the form containing the input
|
||||
if (!form && inputSelector) {
|
||||
const input = document.querySelector(inputSelector);
|
||||
if (input) {
|
||||
form = input.closest('form');
|
||||
}
|
||||
}
|
||||
|
||||
// If still no form, try to find any form on the page
|
||||
if (!form) {
|
||||
form = document.querySelector('form');
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'No form found on the page',
|
||||
};
|
||||
}
|
||||
|
||||
// Focus input if specified
|
||||
if (inputSelector) {
|
||||
const input = document.querySelector(inputSelector);
|
||||
if (input) {
|
||||
input.focus();
|
||||
await sleep(200);
|
||||
}
|
||||
}
|
||||
|
||||
// Try submission based on method
|
||||
let result = null;
|
||||
|
||||
if (submitMethod === 'enter' || submitMethod === 'auto') {
|
||||
result = await tryEnterKeySubmission(form, inputSelector);
|
||||
if (result && result.success) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (submitMethod === 'button' || submitMethod === 'auto') {
|
||||
result = await tryButtonSubmission(form);
|
||||
if (result && result.success) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (submitMethod === 'auto') {
|
||||
result = await tryFormSubmission(form);
|
||||
if (result && result.success) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'All submission methods failed',
|
||||
attemptedMethods: submitMethod === 'auto' ? ['enter', 'button', 'form'] : [submitMethod],
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in submitForm:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try submitting form using Enter key
|
||||
* @param {Element} form - The form element
|
||||
* @param {string} inputSelector - Input selector to focus
|
||||
* @returns {Promise<Object|null>}
|
||||
*/
|
||||
async function tryEnterKeySubmission(form, inputSelector) {
|
||||
try {
|
||||
console.log('🔄 Trying Enter key submission');
|
||||
|
||||
let targetElement = null;
|
||||
|
||||
if (inputSelector) {
|
||||
targetElement = document.querySelector(inputSelector);
|
||||
}
|
||||
|
||||
if (!targetElement) {
|
||||
// Find the first input in the form
|
||||
targetElement = form.querySelector('input[type="text"], input[type="search"], textarea, input:not([type])');
|
||||
}
|
||||
|
||||
if (!targetElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
targetElement.focus();
|
||||
await sleep(200);
|
||||
|
||||
const enterEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
targetElement.dispatchEvent(enterEvent);
|
||||
|
||||
// Also try keypress and keyup for compatibility
|
||||
const enterPress = new KeyboardEvent('keypress', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
targetElement.dispatchEvent(enterPress);
|
||||
|
||||
const enterUp = new KeyboardEvent('keyup', {
|
||||
key: 'Enter',
|
||||
code: 'Enter',
|
||||
keyCode: 13,
|
||||
which: 13,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
targetElement.dispatchEvent(enterUp);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
method: 'enter_key',
|
||||
element: targetElement.tagName.toLowerCase(),
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.debug('Enter key submission failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try submitting form by clicking submit button
|
||||
* @param {Element} form - The form element
|
||||
* @returns {Promise<Object|null>}
|
||||
*/
|
||||
async function tryButtonSubmission(form) {
|
||||
try {
|
||||
console.log('🔄 Trying button submission');
|
||||
|
||||
const buttonSelectors = [
|
||||
'input[type="submit"]',
|
||||
'button[type="submit"]',
|
||||
'button:not([type])', // Default button type is submit
|
||||
'input[value*="Search" i]',
|
||||
'input[value*="Submit" i]',
|
||||
'input[value*="Send" i]',
|
||||
'button:contains("Search")',
|
||||
'button:contains("Submit")',
|
||||
'button:contains("Send")',
|
||||
'.submit-btn',
|
||||
'.search-btn',
|
||||
'.btn-submit',
|
||||
'[role="button"][aria-label*="search" i]',
|
||||
'[role="button"][aria-label*="submit" i]'
|
||||
];
|
||||
|
||||
for (const selector of buttonSelectors) {
|
||||
try {
|
||||
let button = form.querySelector(selector);
|
||||
|
||||
// If not found in form, try the whole document
|
||||
if (!button) {
|
||||
button = document.querySelector(selector);
|
||||
}
|
||||
|
||||
if (button) {
|
||||
button.click();
|
||||
await sleep(300);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
method: 'button_click',
|
||||
selector: selector,
|
||||
element: button.tagName.toLowerCase(),
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (error) {
|
||||
console.debug('Button submission failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try submitting form using form.submit()
|
||||
* @param {Element} form - The form element
|
||||
* @returns {Promise<Object|null>}
|
||||
*/
|
||||
async function tryFormSubmission(form) {
|
||||
try {
|
||||
console.log('🔄 Trying form.submit()');
|
||||
|
||||
form.submit();
|
||||
await sleep(300);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
method: 'form_submit',
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.debug('Form submission failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep utility function
|
||||
* @param {number} ms - Milliseconds to sleep
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Listen for messages from the extension
|
||||
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
|
||||
if (request.action === 'submitForm') {
|
||||
submitForm(request.formSelector, request.inputSelector, request.submitMethod)
|
||||
.then(sendResponse)
|
||||
.catch((error) => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
});
|
||||
});
|
||||
return true; // Indicates async response
|
||||
} else if (request.action === 'form_submit_ping') {
|
||||
sendResponse({ status: 'pong' });
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
147
app/chrome-extension/inject-scripts/user-id-helper.js
Normal file
147
app/chrome-extension/inject-scripts/user-id-helper.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Chrome Extension User ID Helper
|
||||
* This script provides easy access to the Chrome extension user ID in any web page
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Namespace for Chrome extension user ID functionality
|
||||
window.ChromeExtensionUserID = {
|
||||
// Current user ID (will be populated when available)
|
||||
userId: null,
|
||||
|
||||
// Callbacks to execute when user ID becomes available
|
||||
callbacks: [],
|
||||
|
||||
/**
|
||||
* Get the current user ID
|
||||
* @returns {Promise<string|null>} The user ID or null if not available
|
||||
*/
|
||||
async getUserId() {
|
||||
// If already available, return immediately
|
||||
if (this.userId) {
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
// Try to get from sessionStorage first
|
||||
try {
|
||||
const storedUserId = sessionStorage.getItem('chromeExtensionUserId');
|
||||
if (storedUserId) {
|
||||
this.userId = storedUserId;
|
||||
return storedUserId;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore storage errors
|
||||
}
|
||||
|
||||
// Try to get from global window variable
|
||||
if (window.chromeExtensionUserId) {
|
||||
this.userId = window.chromeExtensionUserId;
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
// Request from content script
|
||||
return new Promise((resolve) => {
|
||||
// Set up listener for the custom event
|
||||
const listener = (event) => {
|
||||
if (event.detail && event.detail.userId) {
|
||||
this.userId = event.detail.userId;
|
||||
window.removeEventListener('chromeExtensionUserIdReady', listener);
|
||||
resolve(this.userId);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('chromeExtensionUserIdReady', listener);
|
||||
|
||||
// Also check if it's already available
|
||||
setTimeout(() => {
|
||||
if (window.chromeExtensionUserId) {
|
||||
this.userId = window.chromeExtensionUserId;
|
||||
window.removeEventListener('chromeExtensionUserIdReady', listener);
|
||||
resolve(this.userId);
|
||||
} else {
|
||||
// Timeout after 5 seconds
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('chromeExtensionUserIdReady', listener);
|
||||
resolve(null);
|
||||
}, 5000);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute callback when user ID becomes available
|
||||
* @param {Function} callback - Function to execute with user ID
|
||||
*/
|
||||
onUserIdReady(callback) {
|
||||
if (this.userId) {
|
||||
// Execute immediately if already available
|
||||
callback(this.userId);
|
||||
} else {
|
||||
// Store callback for later execution
|
||||
this.callbacks.push(callback);
|
||||
|
||||
// Try to get user ID
|
||||
this.getUserId().then((userId) => {
|
||||
if (userId) {
|
||||
// Execute all pending callbacks
|
||||
this.callbacks.forEach(cb => cb(userId));
|
||||
this.callbacks = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if user ID is available
|
||||
* @returns {boolean} True if user ID is available
|
||||
*/
|
||||
isAvailable() {
|
||||
return this.userId !== null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get user ID synchronously (only if already loaded)
|
||||
* @returns {string|null} The user ID or null if not loaded
|
||||
*/
|
||||
getUserIdSync() {
|
||||
return this.userId || window.chromeExtensionUserId || null;
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-initialize when script loads
|
||||
window.ChromeExtensionUserID.getUserId().then((userId) => {
|
||||
if (userId) {
|
||||
console.log('Chrome Extension User ID Helper: User ID loaded:', userId);
|
||||
} else {
|
||||
console.log('Chrome Extension User ID Helper: No user ID available');
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for the custom event in case it comes later
|
||||
window.addEventListener('chromeExtensionUserIdReady', (event) => {
|
||||
if (event.detail && event.detail.userId) {
|
||||
window.ChromeExtensionUserID.userId = event.detail.userId;
|
||||
console.log('Chrome Extension User ID Helper: User ID received via event:', event.detail.userId);
|
||||
|
||||
// Execute any pending callbacks
|
||||
window.ChromeExtensionUserID.callbacks.forEach(callback => callback(event.detail.userId));
|
||||
window.ChromeExtensionUserID.callbacks = [];
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
// Also provide a simple global function for easy access
|
||||
window.getChromeExtensionUserId = function() {
|
||||
return window.ChromeExtensionUserID.getUserId();
|
||||
};
|
||||
|
||||
// Provide a synchronous version
|
||||
window.getChromeExtensionUserIdSync = function() {
|
||||
return window.ChromeExtensionUserID.getUserIdSync();
|
||||
};
|
||||
|
||||
console.log('Chrome Extension User ID Helper loaded. Use window.getChromeExtensionUserId() or window.ChromeExtensionUserID.getUserId()');
|
Reference in New Issue
Block a user