您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Detects bot shops and reports them.
当前为
// ==UserScript== // @name Neopets Bot shop reporter // @namespace http://tampermonkey.net/ // @version 1.1 // @description Detects bot shops and reports them. // @author God // @match *://www.neopets.com/browseshop.phtml?owner=* // @match *://www.neopets.com/settings/privacy/report/?offender=* // @grant GM_xmlhttpRequest // @grant window.focus // @connect www.neopets.com // @connect images.neopets.com // ==/UserScript== (function() { 'use strict'; // Banner image URL for bot accounts const BOT_BANNER_URL = 'https://images.neopets.com/new_shopkeepers/0.gif'; let hasSubmitted = false; // Flag to prevent multiple submissions // Function to observe DOM for an element (using CSS or XPath) and execute callback when found function observeForElement(selector, callback, targetNode = document.body, timeout = 30000, isXPath = false) { if (!targetNode) { console.warn(`Target node not found for ${selector}. Retrying...`); setTimeout(() => observeForElement(selector, callback, document.body, timeout, isXPath), 100); return; } let timeoutId; const observer = new MutationObserver((mutations, obs) => { let element; if (isXPath) { const result = document.evaluate(selector, targetNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); element = result.singleNodeValue; } else { element = targetNode.querySelector(selector); } if (element) { obs.disconnect(); clearTimeout(timeoutId); callback(element); } }); observer.observe(targetNode, { childList: true, subtree: true }); // Immediate check let element; if (isXPath) { const result = document.evaluate(selector, targetNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); element = result.singleNodeValue; } else { element = targetNode.querySelector(selector); } if (element) { observer.disconnect(); callback(element); return; } // Timeout timeoutId = setTimeout(() => { observer.disconnect(); console.error(`Timeout: Element ${selector} not found after ${timeout}ms`); console.log('App div content:', targetNode.querySelector('#app')?.innerHTML || 'Empty'); console.log('All forms:', Array.from(targetNode.querySelectorAll('form')).map(form => form.outerHTML)); console.log('Form inputs:', Array.from(targetNode.querySelectorAll('input')).map(el => ({ id: el.id, name: el.name, type: el.type, class: el.className, value: el.value }))); console.log('Form selects:', Array.from(targetNode.querySelectorAll('select')).map(el => ({ id: el.id, name: el.name, class: el.className, value: el.value, html: el.outerHTML, options: Array.from(el.options).map(opt => ({ value: opt.value, text: opt.text })) }))); console.log('Form textareas:', Array.from(targetNode.querySelectorAll('textarea')).map(el => ({ id: el.id, name: el.name, class: el.className, value: el.value }))); console.log('Form buttons:', Array.from(targetNode.querySelectorAll('button')).map(el => ({ id: el.id, name: el.name, type: el.type, class: el.className }))); const captcha = targetNode.querySelector('form[id*="captcha"], div[id*="captcha"], [class*="captcha"]'); if (captcha) { console.warn('Possible CAPTCHA or anti-bot prompt detected:', captcha.outerHTML); } callback(null); }, timeout); } // Function to check if user is logged in function isLoggedIn() { return !!document.querySelector('a[href="/logout.phtml"]'); } // Function to extract username from shop or report page function getUsername() { const url = window.location.href; if (url.includes('browseshop.phtml')) { const reportLink = document.querySelector('a[href*="/autoform_abuse.phtml?abuse=report"]'); if (reportLink) { const match = reportLink.getAttribute('href').match(/offender=([^&]+)/); return match ? match[1] : null; } return null; } else if (url.includes('settings/privacy/report')) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('offender'); } return null; } // Function to log form state for debugging function logFormState(form, stage) { console.log(`Form state at ${stage}:`); console.log('Inputs:', Array.from(form.querySelectorAll('input')).map(el => ({ id: el.id, name: el.name, type: el.type, class: el.className, value: el.value, checked: el.checked }))); console.log('Selects:', Array.from(form.querySelectorAll('select')).map(el => ({ id: el.id, name: el.name, class: el.className, value: el.value, options: Array.from(el.options).map(opt => ({ value: opt.value, text: opt.text })) }))); console.log('Textareas:', Array.from(form.querySelectorAll('textarea')).map(el => ({ id: el.id, name: el.name, class: el.className, value: el.value }))); console.log('Buttons:', Array.from(form.querySelectorAll('button')).map(el => ({ id: el.id, name: el.name, type: el.type, class: el.className }))); } // Function to check for bot banner in DOM function checkBotBanner(callback) { observeForElement('div[align="center"] img[src*="new_shopkeepers"]', (img) => { if (!img) { console.log('No shop banner image found in div[align="center"]. Skipping shop.'); callback(false); return; } const src = img.getAttribute('src'); console.log(`Found shop banner: ${src}`); if (src === BOT_BANNER_URL) { console.log('Bot banner (0.gif) detected.'); callback(true); } else { console.log(`Non-bot banner detected (${src}). Skipping shop.`); callback(false); } }, document.body, 10000); } // Function to fill and submit the report form function fillReportForm(username, reportWindow = window) { if (hasSubmitted) { console.log(`Form already submitted for ${username}. Skipping.`); return; } console.log(`Filling report form for user: ${username}`); console.log(`Report type from URL: ${new URLSearchParams(reportWindow.location.search).get('reportType') || 'None'}`); console.log(`Initial window location: ${reportWindow.location.href}`); // Capture JavaScript errors reportWindow.onerror = function(message, source, lineno, colno, error) { console.error(`Page JavaScript error: ${message} at ${source}:${lineno}:${colno}`, error); }; // Wait for form to load in #app observeForElement('#app form', (form) => { if (!form) { console.error('Report form not found in #app. Please fill the form manually.'); return; } console.log('Report form found:', form.outerHTML); logFormState(form, 'pre-fill'); // Add submit event listener for debugging form.addEventListener('submit', () => { console.log('Form submit event triggered'); console.log('Form data:', Array.from(new FormData(form).entries()).map(([key, value]) => ({ key, value }))); }); // Check no_user checkbox observeForElement('#no_user', (checkbox) => { if (!checkbox) { console.error('No_user checkbox not found, trying XPath'); observeForElement('//input[@id="no_user" or @name="no_user"]', (xpathCheckbox) => { if (!xpathCheckbox) { console.error('No_user checkbox not found with XPath. Please fill the form manually.'); return; } handleCheckbox(xpathCheckbox); }, reportWindow.document, 30000, true); return; } handleCheckbox(checkbox); }, reportWindow.document, 30000); function handleCheckbox(checkbox) { if (!checkbox.checked) { checkbox.checked = true; checkbox.dispatchEvent(new Event('change', { bubbles: true })); console.log('Checked no_user checkbox'); } // Select Other from report_type observeForElement('#report_type', (select) => { if (!select) { console.error('Report_type dropdown not found, trying XPath'); observeForElement('//select[@id="report_type" or @name="report_type"]', (xpathSelect) => { if (!xpathSelect) { console.error('Report_type dropdown not found with XPath. Please fill the form manually.'); return; } handleSelect(xpathSelect, 'report_type'); }, reportWindow.document, 30000, true); return; } handleSelect(select, 'report_type'); }, reportWindow.document, 30000); function handleSelect(select, type) { const options = Array.from(select.options).map(opt => ({ value: opt.value, text: opt.text })); console.log(`${type} options:`, options); console.log(`${type} HTML:`, select.outerHTML); if (select.querySelector('option[value="Other"]')) { select.value = 'Other'; select.dispatchEvent(new Event('change', { bubbles: true })); console.log(`Selected Other in ${type}`); } else { console.error(`Option "Other" not found in ${type}, selecting last option as fallback`); select.value = options[options.length - 1].value; select.dispatchEvent(new Event('change', { bubbles: true })); console.log(`Selected fallback option: ${select.value}`); } // Select Other from report_place if report_type was successful if (type === 'report_type') { observeForElement('#report_place', (placeSelect) => { if (!placeSelect) { console.error('Report_place dropdown not found, trying XPath'); observeForElement('//select[@id="report_place" or @name="report_place"]', (xpathPlaceSelect) => { if (!xpathPlaceSelect) { console.error('Report_place dropdown not found with XPath. Please fill the form manually.'); return; } handleSelect(xpathPlaceSelect, 'report_place'); }, reportWindow.document, 30000, true); return; } handleSelect(placeSelect, 'report_place'); }, reportWindow.document, 30000); } // Try to fill textarea, but proceed if not found if (type === 'report_place') { observeForElement('#report_body', (textarea) => { if (!textarea) { console.error('Report_body textarea not found, trying XPath'); observeForElement('//textarea[@id="report_body" or @name="report_body"]', (xpathTextarea) => { if (!xpathTextarea) { console.warn('No textarea found, attempting submission without report_body'); submitForm(form); return; } handleTextarea(xpathTextarea); }, reportWindow.document, 5000, true); return; } handleTextarea(textarea); }, reportWindow.document, 5000); } } function handleTextarea(textarea) { const comment = "This is a bot account with a default shop banner and minimal shop activity, likely used for automated tasks like dailies or Foodclub betting."; textarea.value = comment; textarea.dispatchEvent(new Event('input', { bubbles: true })); console.log('Filled report_body'); submitForm(form); } function submitForm(form) { observeForElement('#email_btn', (button) => { if (!button) { console.error('Submit button not found, trying XPath'); observeForElement('//button[@id="email_btn" or @name="email_btn" or @type="submit"]', (xpathButton) => { if (!xpathButton) { console.error('Submit button not found with XPath. Please submit the form manually.'); return; } handleSubmit(form, xpathButton); }, reportWindow.document, 30000, true); return; } handleSubmit(form, button); }, reportWindow.document, 30000); } function handleSubmit(form, button) { if (hasSubmitted) { console.log('Form already submitted. Skipping.'); return; } hasSubmitted = true; console.log('Submitting report'); logFormState(form, 'pre-submit'); setTimeout(() => { button.click(); console.log('Submit button clicked'); setTimeout(() => { console.log(`Post-submit window location: ${reportWindow.location.href}`); const successDiv = reportWindow.document.querySelector('#submitResponse_report'); if (successDiv && successDiv.textContent.includes('Your request has been successfully submitted!')) { console.log('Report submission successful for', username); logFormState(form, 'post-submit-success'); setTimeout(() => { console.log('Closing report tab'); reportWindow.close(); }, 1000); } else { console.log('No success message found for', username); console.log('Report page HTML after submission:', reportWindow.document.documentElement.outerHTML); logFormState(form, 'post-submit-failure'); const errorMsg = reportWindow.document.querySelector('.error, .alert, [class*="error"], [class*="alert"]'); if (errorMsg) { console.error('Possible submission error detected:', errorMsg.outerHTML); } if (!reportWindow.location.href.includes('settings/privacy/report')) { console.error('Possible redirect detected after submission:', reportWindow.location.href); } hasSubmitted = false; // Allow retry if submission fails } }, 3000); }, 1000); } } }, reportWindow.document, 30000); } // Main function to handle both shop and report pages function main() { console.log('Neopets Shop Report Autofill script started at', new Date().toLocaleString('en-AU', { timeZone: 'Australia/Sydney' })); // Check if user is logged in if (!isLoggedIn()) { console.error('User not logged in. Please log in to Neopets.'); return; } const url = window.location.href; if (url.includes('browseshop.phtml')) { // Shop page: check for bot banner and item conditions checkBotBanner((isBotBanner) => { if (!isBotBanner) { console.log('No bot banner detected. Skipping shop.'); return; } // Check for item table or no-items message observeForElement('table[align="center"][border="0"][cellpadding="3"], span[style="color: black;"] > b', (element) => { let shouldReport = false; if (element.tagName.toLowerCase() === 'table') { const items = element.querySelectorAll('td').length / 2; // Each item has 2 td elements console.log(`Found ${items} items in shop`); if (items <= 20) { shouldReport = true; } } else if (element.textContent.includes('There are no items for sale in this shop!')) { console.log('No items in shop'); shouldReport = true; } if (shouldReport) { const username = getUsername(); if (username) { console.log(`Reporting user: ${username}`); const reportUrl = `https://www.neopets.com/settings/privacy/report/?offender=${encodeURIComponent(username)}&reportType=Shops`; console.log(`Opening report URL in new tab: ${reportUrl}`); const reportWindow = window.open(reportUrl, '_blank'); if (!reportWindow) { console.error('Failed to open report page. Please allow pop-ups.'); return; } reportWindow.focus(); reportWindow.onload = function() { console.log(`Report page loaded for ${username}`); fillReportForm(username, reportWindow); }; } else { console.error('Could not extract username from report link'); } } else { console.log('Shop has more than 20 items, not reporting'); } }, document.body, 10000); }); } else if (url.includes('settings/privacy/report')) { // Report page: directly fill the form const username = getUsername(); if (username) { console.log(`Report type from URL: ${new URLSearchParams(window.location.search).get('reportType') || 'None'}`); fillReportForm(username); } else { console.error('No offender username found in URL'); } } } // Start script when page loads if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();