better logging and detecting

This commit is contained in:
2026-02-14 20:30:12 +01:00
parent 6e5cfe02fa
commit ae87077519
2 changed files with 172 additions and 38 deletions
+19
View File
@@ -25,3 +25,22 @@ name = "Immaculate Vegan"
category = "Newsletter" category = "Newsletter"
url = "https://immaculatevegan.com/blogs/magazine" url = "https://immaculatevegan.com/blogs/magazine"
needsPreSubmitAction = false needsPreSubmitAction = false
[[sites]]
name = "Gate6"
category = "Newsletter"
url = "https://gate6.vn/"
needsPreSubmitAction = false
[[sites]]
name = "Kulala Land"
category = "Newsletter"
url = "https://kulalaland.com/"
needsPreSubmitAction = false
[[sites]]
name = "sneakywholefoods"
category = "Newsletter"
url = "https://www.sneakywholefoods.com/"
needsPreSubmitAction = false
+153 -38
View File
@@ -326,13 +326,8 @@ async function detectFormFields(page) {
}, FIELD_PATTERNS); }, FIELD_PATTERNS);
const detectedCount = Object.keys(detectedFields).length; const detectedCount = Object.keys(detectedFields).length;
if (detectedCount > 0) { if (detectedCount === 0) {
console.log(` ✅ Auto-detected ${detectedCount} fields:`); console.log(' No fields detected');
for (const [field, info] of Object.entries(detectedFields)) {
console.log(` - ${field}: ${info.selector} (score: ${info.confidence})`);
}
} else {
console.log(' ⚠️ No fields auto-detected');
} }
return detectedFields; return detectedFields;
@@ -409,7 +404,9 @@ async function detectSubmitButton(page) {
}); });
if (submitSelector) { if (submitSelector) {
console.log(`Auto-detected submit button: ${submitSelector}`); console.log(`Submit button found: ${submitSelector}`);
} else {
console.log(' ❌ No submit button found');
} }
return submitSelector; return submitSelector;
@@ -417,7 +414,26 @@ async function detectSubmitButton(page) {
async function main() { async function main() {
const config = loadConfig(); const config = loadConfig();
const sitesToProcess = config.sites || []; let sitesToProcess = config.sites || [];
const targetSite = process.argv[2];
const logToFile = process.argv.includes('--log');
const logFile = logToFile ? path.join(__dirname, `run_${new Date().toISOString().replace(/[:.]/g, '-')}.log`) : null;
if (targetSite && !targetSite.startsWith('--')) {
sitesToProcess = sitesToProcess.filter(site =>
site.name.toLowerCase().includes(targetSite.toLowerCase()) ||
site.url.toLowerCase().includes(targetSite.toLowerCase())
);
if (sitesToProcess.length === 0) {
console.error(`❌ No site found matching "${targetSite}"`);
console.log('Available sites:');
config.sites.forEach(site => console.log(` - ${site.name}: ${site.url}`));
return;
}
console.log(`🎯 Filtering to ${sitesToProcess.length} site(s) matching "${targetSite}"`);
}
const browserConfig = config.browser || {}; const browserConfig = config.browser || {};
const globalCreds = config.global_credentials || {}; const globalCreds = config.global_credentials || {};
const userDataDir = browserConfig.userDataDir; const userDataDir = browserConfig.userDataDir;
@@ -427,6 +443,36 @@ async function main() {
return; return;
} }
if (logToFile) {
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
const writeToLog = (type, args) => {
const message = args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' ');
fs.appendFileSync(logFile, `[${new Date().toISOString()}] [${type}] ${message}\n`);
};
console.log = (...args) => {
writeToLog('LOG', args);
originalLog.apply(console, args);
};
console.error = (...args) => {
writeToLog('ERROR', args);
originalError.apply(console, args);
};
console.warn = (...args) => {
writeToLog('WARN', args);
originalWarn.apply(console, args);
};
console.log(`📝 Logging to file: ${logFile}`);
}
const failedSites = [];
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
headless: false, headless: false,
userDataDir: userDataDir, userDataDir: userDataDir,
@@ -436,6 +482,8 @@ async function main() {
try { try {
let isFirstSite = true; let isFirstSite = true;
for (const site of sitesToProcess) { for (const site of sitesToProcess) {
let siteFailed = false;
let failureReason = '';
let creds; let creds;
if (site.credentials && site.credentials.username) { if (site.credentials && site.credentials.username) {
creds = { ...globalCreds, ...site.credentials }; creds = { ...globalCreds, ...site.credentials };
@@ -451,7 +499,8 @@ async function main() {
console.log('Filled in missing credentials with random data.'); console.log('Filled in missing credentials with random data.');
} }
console.log(`\n--- Processing Site: ${site.name} (Category: ${site.category || 'N/A'}) ---`); console.log(`\n${site.url}`);
console.log(`${'-'.repeat(70)}`);
const page = isFirstSite ? (await browser.pages())[0] : await browser.newPage(); const page = isFirstSite ? (await browser.pages())[0] : await browser.newPage();
isFirstSite = false; isFirstSite = false;
@@ -461,47 +510,78 @@ async function main() {
} }
await page.goto(site.url, { waitUntil: 'networkidle2' }); await page.goto(site.url, { waitUntil: 'networkidle2' });
console.log('Dynamically filling out the form...'); console.log('\n🔍 DETECTED FIELDS:');
let fieldsToFill = {}; let fieldsToFill = {};
let detectedFields = {};
const definedLocators = site.locators || {}; const definedLocators = site.locators || {};
if (site.autoDetect !== false) { if (site.autoDetect !== false) {
const detectedFields = await detectFormFields(page); detectedFields = await detectFormFields(page);
for (const [fieldName, fieldInfo] of Object.entries(detectedFields)) { for (const [fieldName, fieldInfo] of Object.entries(detectedFields)) {
fieldsToFill[fieldName] = fieldInfo.selector; fieldsToFill[fieldName] = fieldInfo.selector;
} }
} }
const hasEmailField = fieldsToFill.email || fieldsToFill.confirmEmail;
if (!hasEmailField) {
siteFailed = true;
failureReason = 'No email field detected';
console.warn(`\n${failureReason}`);
}
const hasSearchInput = await page.evaluate(() => {
const inputs = document.querySelectorAll('input[type="search"], input[name*="search"], input[id*="search"]');
return inputs.length > 0;
});
if (hasSearchInput && !hasEmailField) {
console.warn(` ⚠️ WARNING: This appears to be a SEARCH form, not a newsletter signup!`);
siteFailed = true;
failureReason = failureReason || 'Detected search form instead of newsletter';
}
for (const [fieldName, locator] of Object.entries(definedLocators)) { for (const [fieldName, locator] of Object.entries(definedLocators)) {
if (fieldName !== 'submit') { if (fieldName !== 'submit') {
fieldsToFill[fieldName] = parseLocator(locator); fieldsToFill[fieldName] = parseLocator(locator);
} }
} }
for (const [fieldName, selector] of Object.entries(fieldsToFill)) { if (Object.keys(fieldsToFill).length > 0) {
let valueToFill = creds[fieldName]; console.log(`\n ${'FIELD'.padEnd(12)} | ${'SELECTOR'.padEnd(35)} | ${'CONF'.padEnd(4)} | VALUE`);
console.log(' ' + '-'.repeat(75));
if (fieldName === 'confirmEmail') { for (const [fieldName, fieldInfo] of Object.entries(detectedFields)) {
valueToFill = creds['email']; let valueToFill = creds[fieldName];
}
if (fieldName === 'confirmEmail') {
if (selector && valueToFill) { valueToFill = creds['email'];
try { }
console.log(` - Filling field: ${fieldName} with "${valueToFill}"`);
await page.evaluate((sel, val) => { const selector = fieldInfo.selector.length > 34 ? fieldInfo.selector.substring(0, 31) + '...' : fieldInfo.selector;
const el = document.querySelector(sel); const value = valueToFill ? valueToFill.substring(0, 20) : 'N/A';
if (el) {
el.value = val; console.log(` ${fieldName.padEnd(12)} | ${selector.padEnd(35)} | ${fieldInfo.confidence.toString().padEnd(4)} | ${value}`);
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true })); if (valueToFill) {
} try {
}, selector, valueToFill); await page.evaluate((sel, val) => {
} catch (e) { const el = document.querySelector(sel);
console.warn(` - Could not fill field '${fieldName}': ${e.message}`); if (el) {
el.value = val;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}
}, fieldInfo.selector, valueToFill);
} catch (e) {
console.warn(` ✗ Failed to fill ${fieldName}: ${e.message}`);
siteFailed = true;
failureReason = `Failed to fill ${fieldName}: ${e.message}`;
}
} }
} }
console.log(' ' + '-'.repeat(75));
} }
let submitSelector = null; let submitSelector = null;
@@ -512,19 +592,54 @@ async function main() {
} }
if (submitSelector) { if (submitSelector) {
console.log('Form filled. Attempting to submit...'); try {
await page.click(submitSelector); await page.click(submitSelector);
console.log(` Submission attempt for ${site.name} is complete.`); console.log(` Submitted (${submitSelector})`);
} catch (submitError) {
siteFailed = true;
failureReason = failureReason || `Submit failed: ${submitError.message}`;
console.error(` ✗ Submit failed: ${submitError.message}`);
}
} else { } else {
console.warn('⚠️ No submit button found.'); siteFailed = true;
failureReason = failureReason || 'No submit button found';
console.warn(`${failureReason}`);
}
if (siteFailed) {
failedSites.push({
name: site.name,
url: site.url,
reason: failureReason,
timestamp: new Date().toISOString()
});
} }
await new Promise(r => setTimeout(r, 2000)); await new Promise(r => setTimeout(r, 2000));
} }
} catch(error) { } catch(error) {
console.error('❌ A critical error occurred:', error.message); console.error('Error:', error.message);
} finally { } finally {
console.log('\n🎉 All tasks complete.'); if (failedSites.length > 0) {
const reportPath = path.join(__dirname, 'failed_sites.txt');
const now = new Date();
const dateStr = now.toLocaleDateString('en-GB');
const timeStr = now.toLocaleTimeString('en-GB');
let reportContent = '';
if (!fs.existsSync(reportPath)) {
reportContent = 'DATE | TIME | WEBSITE | REASON\n';
reportContent += '-'.repeat(100) + '\n';
}
for (const site of failedSites) {
const website = `${site.name} (${site.url})`.substring(0, 32).padEnd(32);
reportContent += `${dateStr} | ${timeStr} | ${website} | ${site.reason}\n`;
}
fs.appendFileSync(reportPath, reportContent);
console.log(`\n${failedSites.length} site(s) failed. See: failed_sites.txt`);
}
} }
} }