Загрузка данных
(async function() {
// ================= SETTINGS =================
const START_PAGE = 20481; // Starting page
const END_PAGE = 22400; // Ending page (last)
const TARGET_COLOR = "#FFA000"; // Color HEX code
// Arrays for sorting accounts
const group3to12 = [];
const group12to36 = [];
const group36plus = [];
// ================= HELPER FUNCTIONS =================
const delay = ms => new Promise(res => setTimeout(res, ms));
const mins = m => m * 60 * 1000;
const currentDate = new Date();
function parseDateString(dateStr) {
const match = dateStr.match(/(\d{2})[-./](\d{2})[-./](\d{4})/);
if (match) {
const day = parseInt(match[1], 10);
const month = parseInt(match[2], 10) - 1;
const year = parseInt(match[3], 10);
return new Date(year, month, day);
}
return null;
}
function getMonthsDiff(pastDate) {
const diffYears = currentDate.getFullYear() - pastDate.getFullYear();
const diffMonths = currentDate.getMonth() - pastDate.getMonth();
let totalMonths = (diffYears * 12) + diffMonths;
if (currentDate.getDate() < pastDate.getDate()) {
totalMonths--;
}
return totalMonths;
}
// Smart page parsing
function extractDataFromHTML(html) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const spans = doc.querySelectorAll('span');
const targetSpans = Array.from(spans).filter(span => {
const styleAttr = span.getAttribute('style');
if (!styleAttr) return false;
const formattedStyle = styleAttr.replace(/\s/g, '').toUpperCase();
return formattedStyle.includes(TARGET_COLOR.toUpperCase()) || formattedStyle.includes('RGB(255,160,0)');
});
targetSpans.forEach(span => {
const nickname = span.textContent.trim();
if (/^\d+$/.test(nickname)) {
return;
}
let userCard = null;
const dateRegex = /\d{2}[-./]\d{2}[-./]\d{4}/g;
// Classic table layout
if (span.closest('tr')) {
userCard = span.closest('tr');
} else {
// Search for individual user container
let current = span.parentElement;
while (current && current.tagName !== 'BODY') {
const text = current.textContent;
const datesCount = (text.match(dateRegex) || []).length;
if (datesCount >= 1 && datesCount <= 2) {
const parentText = current.parentElement ? current.parentElement.textContent : "";
const parentDatesCount = (parentText.match(dateRegex) || []).length;
// If the parent has more than 2 dates, we have moved out to the general list.
// Capture the card.
if (parentDatesCount > 2) {
userCard = current;
break;
}
}
current = current.parentElement;
}
// Fallback method
if (!userCard) {
userCard = span.closest('.row') || span.parentElement;
}
}
if (userCard) {
// Collect text only from the found card
const allTexts = Array.from(userCard.querySelectorAll('*')).map(el => el.textContent.trim());
let targetDateStr = null;
// Grab the last date (Last visit)
for (let i = allTexts.length - 1; i >= 0; i--) {
const match = allTexts[i].match(/\d{2}[-./]\d{2}[-./]\d{4}/);
if (match) {
targetDateStr = match[0];
break;
}
}
if (targetDateStr) {
const parsedDate = parseDateString(targetDateStr);
if (parsedDate) {
const monthsDiff = getMonthsDiff(parsedDate);
if (monthsDiff >= 3 && monthsDiff < 12) {
group3to12.push(nickname);
} else if (monthsDiff >= 12 && monthsDiff < 36) {
group12to36.push(nickname);
} else if (monthsDiff >= 36) {
group36plus.push(nickname);
}
}
}
}
});
}
// ================= FILE OPERATIONS =================
async function updateRealTimeFile(fileHandle, dataArray) {
if (!fileHandle || dataArray.length === 0) return;
try {
// Remove duplicates
const uniqueContent = [...new Set(dataArray)].join('\n');
const writable = await fileHandle.createWritable();
await writable.write(uniqueContent);
await writable.close();
} catch (err) {
console.error("%c[WRITE ERROR]%c Failed to update file:", 'color: #dc3545', 'color: inherit', err);
}
}
// ================= LAUNCH AND INTERFACE =================
async function startProcess() {
let dirHandle;
let handle3to12, handle12to36, handle36plus;
try {
// Request save folder from the user
dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
// Create files in the selected folder
handle3to12 = await dirHandle.getFileHandle('accounts_3_to_12_months.txt', { create: true });
handle12to36 = await dirHandle.getFileHandle('accounts_12_to_36_months.txt', { create: true });
handle36plus = await dirHandle.getFileHandle('accounts_36plus_months.txt', { create: true });
console.log(`%c[FILES CREATED]%c Files successfully linked to the selected folder.`, 'color: #28a745', 'color: inherit');
} catch (error) {
console.error("Parsing cancelled. Folder access is required for the script to work.", error);
return; // Stop execution if user denied access
}
let errorStage = 0;
let page = START_PAGE;
// Variables to track array changes (to avoid unnecessary rewrites)
let lastSizes = { g1: 0, g2: 0, g3: 0 };
console.log(`%c[START]%c Starting parsing from page ${START_PAGE} to ${END_PAGE}...`, 'font-weight: bold; color: #6f42c1', 'color: inherit');
while (page <= END_PAGE) {
const url = `https://aimware.net/forum/memberlist/page/${page}/sort/regdate/order/ascending`;
try {
console.log(`%c[SCANNING]%c Page ${page}...`, 'color: #007bff', 'color: inherit');
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP Error ${response.status}`);
}
const html = await response.text();
extractDataFromHTML(html);
console.log(`%c[SUCCESS]%c Page ${page} processed.`, 'color: #28a745', 'color: inherit');
// Update files every 5 pages or if it is the last page
if (page % 5 === 0 || page === END_PAGE) {
if (group3to12.length > lastSizes.g1) {
await updateRealTimeFile(handle3to12, group3to12);
lastSizes.g1 = group3to12.length;
}
if (group12to36.length > lastSizes.g2) {
await updateRealTimeFile(handle12to36, group12to36);
lastSizes.g2 = group12to36.length;
}
if (group36plus.length > lastSizes.g3) {
await updateRealTimeFile(handle36plus, group36plus);
lastSizes.g3 = group36plus.length;
}
console.log(`%c[SAVING]%c Files updated on disk (page ${page}).`, 'color: #17a2b8', 'color: inherit');
}
errorStage = 0;
page++;
// Anti-spam safety delay
await delay(1500);
} catch (error) {
console.error(`%c[ERROR]%c Error on page ${page}:`, 'color: #dc3545', 'color: inherit', error.message);
if (errorStage === 0) {
console.warn(`%c[WAITING]%c Waiting 5 minutes before retrying page ${page}...`, 'color: #fd7e14', 'color: inherit');
await delay(mins(5));
errorStage = 1;
} else if (errorStage === 1) {
console.warn(`%c[SKIPPING]%c Error again. Skipping page ${page}, moving to the next and waiting 10 minutes...`, 'color: #fd7e14', 'color: inherit');
page++;
await delay(mins(10));
errorStage = 2;
} else if (errorStage === 2) {
console.warn(`%c[SKIPPING]%c Error again. Skipping page ${page}, moving to the next and waiting 30 minutes...`, 'color: #dc3545', 'color: inherit');
page++;
await delay(mins(30));
errorStage = 3;
} else {
console.warn(`%c[SKIPPING]%c Cyclical error. Skipping page ${page}, moving to the next and waiting 10 minutes...`, 'color: #fd7e14', 'color: inherit');
page++;
await delay(mins(10));
}
}
}
console.log(`%c[FINISH]%c All pages processed! Files are up to date and located in your selected folder.`, 'font-weight: bold; color: #28a745', 'color: inherit');
}
// Create a button to bypass File System API restriction
const uiOverlay = document.createElement('div');
uiOverlay.style.cssText = 'position:fixed; top:0; left:0; width:100vw; height:100vh; background:rgba(0,0,0,0.85); z-index:999999; display:flex; flex-direction:column; justify-content:center; align-items:center; color:#fff; font-family:sans-serif;';
const startBtn = document.createElement('button');
startBtn.textContent = 'START PARSING (SELECT FOLDER)';
startBtn.style.cssText = 'padding:20px 40px; font-size:24px; font-weight:bold; cursor:pointer; background:#28a745; color:#fff; border:none; border-radius:10px; box-shadow: 0 4px 15px rgba(40,167,69,0.5); transition: 0.2s;';
startBtn.onmouseover = () => startBtn.style.transform = 'scale(1.05)';
startBtn.onmouseout = () => startBtn.style.transform = 'scale(1)';
const infoText = document.createElement('p');
infoText.innerHTML = 'Click the button and select an <b>empty folder</b> on your PC.<br>The script will create 3 files in it and update them in real time.';
infoText.style.cssText = 'margin-top:20px; font-size:16px; text-align:center; line-height:1.5; color:#ccc;';
uiOverlay.appendChild(startBtn);
uiOverlay.appendChild(infoText);
document.body.appendChild(uiOverlay);
startBtn.addEventListener('click', async () => {
document.body.removeChild(uiOverlay);
await startProcess();
});
})();