DO NOT LEAK!
// ==UserScript==
// @name NIHOHOHONGI
// @namespace Too Lazy(KingC)
// @version 7.4
// @description DO NOT LEAK!
// @match https://www.irodori-online.jpf.go.jp/*
// @run-at document-end
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('[Irodori Auto v7.4] IMPERVIOUS: 3s intervals - No backtracking');
if (!location.pathname.includes('/learning/')) {
console.log('[Irodori] Not learning page — disabled');
return;
}
const CONFIG = {
LOOP_INTERVAL: 3500, // 🔥 CHANGED: 3.5 seconds (3500ms)
ACTION_COOLDOWN: 3500,
PAGE_SETTLE: 3500,
BACK_BLOCK_LIST: ['g-ui2-retry', 'return', '戻る', '前の', 'back', 'retry', '練習', 'practice', 'reset']
};
let lastAction = 0;
let recentUrl = location.href;
const now = () => Date.now();
// [safeClick, findNextOrArrow, findCheckmarkButton, quickAnswer, resetMedia, masterLoop - SAME AS BEFORE]
function safeClick(el, label = 'button') {
if (el.classList.contains('g-ui2-retry')) {
console.log(`[🚫 BLOCKED] g-ui2-retry (${label})`);
return false;
}
const text = (el.textContent || el.innerText || el.getAttribute('aria-label') || '').toLowerCase();
const backKeywords = CONFIG.BACK_BLOCK_LIST;
for (let keyword of backKeywords) {
if (text.includes(keyword.toLowerCase())) {
console.log(`[🚫 BLOCKED] Back text "${keyword}" in ${label}`);
return false;
}
}
if (el.closest('.g-ui2-retry, [class*="retry"], [class*="back"], [title*="back"]')) {
console.log(`[🚫 BLOCKED] Parent is back/retry (${label})`);
return false;
}
const href = el.getAttribute('href') || el.dataset.href;
if (href && (href.includes('previous') || href.includes('back'))) {
console.log(`[🚫 BLOCKED] href back navigation (${label})`);
return false;
}
if (el.disabled || !el.offsetParent || el.style.display === 'none') {
return false;
}
try {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
setTimeout(() => {
el.focus();
el.dispatchEvent(new MouseEvent('click', { bubbles: true }));
console.log(`[✅ CLICKED] ${label}`);
recentUrl = location.href;
}, 50);
return true;
} catch (e) {
console.error(`[Irodori] Click failed ${label}`);
return false;
}
}
function findNextOrArrow() {
const candidates = document.querySelectorAll('button:not(.g-ui2-retry):not([class*="retry"]):not([class*="back"]), a:not(.g-ui2-retry), [role="button"]:not(.g-ui2-retry)');
return Array.from(candidates).find(el => {
const text = (el.textContent || el.innerText || el.getAttribute('aria-label') || '').trim().toLowerCase();
const classes = el.className.toLowerCase();
const forwardTexts = /次へ|つぎへ|next|進む|forward|▶|→|right|advance|continue/i;
const hasRightArrow = el.querySelector('svg path[d*="M9 5l7 7-7 7|M 10 8 L 18 16|right|chevronright|arrowright"]');
const backTexts = /back|前の|戻る|return|retry|練習|practice|reset|previous/i;
if (backTexts.test(text + ' ' + classes)) {
return false;
}
const rect = el.getBoundingClientRect();
const isBottomForward = rect.bottom > window.innerHeight * 0.75 &&
(forwardTexts.test(text + ' ' + classes) || hasRightArrow);
return isBottomForward && !el.disabled && el.offsetParent;
});
}
function findCheckmarkButton() {
const candidates = document.querySelectorAll('button:not(.g-ui2-retry):not([class*="retry"]), .g-ui-button-check:not(.g-ui2-retry), [role="button"]:not(.g-ui2-retry)');
return Array.from(candidates).find(el => {
const text = (el.textContent || el.innerText || '').trim().toLowerCase();
const aria = (el.getAttribute('aria-label') || '').toLowerCase();
const classes = el.className.toLowerCase();
if (classes.includes('g-ui-button-check')) {
console.log('[🎯 FOUND] Exact g-ui-button-check!');
return !el.disabled && el.offsetParent;
}
const checkTexts = /確認|チェック|check|✓|✔|ok|correct|正解|submit|send/i;
const backTexts = /back|前の|戻る|return|retry|練習|reset/i;
if (backTexts.test(text + ' ' + aria + ' ' + classes)) {
return false;
}
const hasCheckmark = el.querySelector('svg path[d*="M9 16l-4-4|check|tick"]') ||
el.innerHTML.includes('✓') || el.innerHTML.includes('✔');
const rect = el.getBoundingClientRect();
const isActionButton = rect.bottom > window.innerHeight * 0.7;
return (checkTexts.test(text + ' ' + aria) || hasCheckmark) &&
isActionButton && !el.disabled && el.offsetParent;
});
}
function quickAnswer() {
const radio = document.querySelector('input[type="radio"]:not(:checked)');
if (radio && safeClick(radio.parentElement || radio, 'radio')) return true;
const checkbox = document.querySelector('input[type="checkbox"]:not(:checked)');
if (checkbox && safeClick(checkbox.parentElement || checkbox, 'checkbox')) return true;
return true;
}
function resetMedia() {
document.querySelectorAll('audio, video').forEach(m => {
m.pause(); m.currentTime = 0; m.muted = true;
});
}
function masterLoop() {
if (location.href !== recentUrl && location.href.includes('previous')) {
console.log('[🚨 URL BACK DETECTED - PAUSED]');
return;
}
resetMedia();
let nextBtn = findNextOrArrow();
if (nextBtn && now() - lastAction > CONFIG.ACTION_COOLDOWN / 2) {
if (safeClick(nextBtn, 'NEXT/ARROW')) {
lastAction = now();
return;
}
}
if (now() - lastAction >= CONFIG.ACTION_COOLDOWN) {
let checkBtn = findCheckmarkButton();
if (checkBtn && safeClick(checkBtn, 'CHECKMARK')) {
lastAction = now();
return;
}
}
if (now() - lastAction > CONFIG.ACTION_COOLDOWN * 1.5) {
quickAnswer();
lastAction = now();
}
}
// LAUNCH with 4s intervals
setTimeout(() => {
console.log('[Irodori v7.4] 🚀 4-SECOND INTERVALS ACTIVE - IMPERVIOUS MODE');
setInterval(masterLoop, CONFIG.LOOP_INTERVAL); // Now 4000ms = 4s
}, CONFIG.PAGE_SETTLE);
const observer = new MutationObserver(() => setTimeout(masterLoop, 300));
observer.observe(document.body, { childList: true, subtree: true });
})();