AutoYandexPraktikum/script.js
2026-03-27 11:40:53 +03:00

662 lines
No EOL
24 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(async function autoClickSequential() {
// Настройки
const CLICK_DELAY = 800;
const CHECK_INTERVAL = 500;
const MAX_WAIT_TIME = 5000;
const PAGE_LOAD_DELAY = 2000;
// Селекторы
const BUTTON_SELECTOR = 'button.content-expander__button';
const NEXT_LESSON_SELECTOR = 'button[data-test-id="next-lesson-control-button"]';
const FEEDBACK_FORM_SELECTOR = 'section.quiz_type_feedback';
const FEEDBACK_NEXT_SELECTOR = 'button.quiz__move-on';
const FEEDBACK_TEXTAREA_SELECTOR = 'textarea.input__control';
const QUIZ_FORM_SELECTOR = 'form.quiz_type_select';
const QUIZ_SUBMIT_SELECTOR = 'button.quiz__submit';
let clickCount = 0;
let lessonCount = 0;
let isRunning = true;
let clickedButtons = new WeakSet();
let handledQuizzes = new WeakSet();
let handledFeedbacks = new WeakSet();
function findNewButton() {
const buttons = document.querySelectorAll(BUTTON_SELECTOR);
for (const button of buttons) {
if (clickedButtons.has(button)) continue;
if (button.offsetParent !== null) {
return button;
}
}
return null;
}
function findNextLessonButton() {
const button = document.querySelector(NEXT_LESSON_SELECTOR);
if (button && button.offsetParent !== null) {
return button;
}
return null;
}
function findFeedbackForm() {
const forms = document.querySelectorAll(FEEDBACK_FORM_SELECTOR);
for (const form of forms) {
if (handledFeedbacks.has(form)) continue;
if (form.offsetParent !== null) {
return form;
}
}
return null;
}
function isQuizAnswered(form) {
const checkedRadio = form.querySelector('input[type="radio"][aria-checked="true"]');
if (checkedRadio) return true;
if (form.classList.contains('quiz_answered')) return true;
const submitBtn = form.querySelector(QUIZ_SUBMIT_SELECTOR);
if (!submitBtn) return true;
return false;
}
function findTextQuiz() {
const quizzes = document.querySelectorAll('section.quiz_type_text');
for (const quiz of quizzes) {
if (handledQuizzes.has(quiz)) continue;
if (quiz.classList.contains('quiz_answered')) { handledQuizzes.add(quiz); continue; }
const submitBtn = quiz.querySelector('button.quiz__submit');
if (!submitBtn) { handledQuizzes.add(quiz); continue; }
if (quiz.offsetParent !== null) return quiz;
}
return null;
}
async function handleTextQuiz(quiz) {
console.log('✏️ Текстовый квиз...');
const textarea = quiz.querySelector('textarea.quiz__input-control');
if (textarea) {
simulateTyping(textarea, 'не знаю');
console.log('✓ Введён ответ');
await sleep(400);
}
const submitBtn = quiz.querySelector('button.quiz__submit');
if (submitBtn && !submitBtn.disabled) {
submitBtn.click();
console.log('✓ Нажата кнопка "Узнать ответ"');
} else if (submitBtn) {
submitBtn.disabled = false;
submitBtn.click();
console.log('✓ Принудительный клик "Узнать ответ"');
}
handledQuizzes.add(quiz);
await sleep(CLICK_DELAY);
return true;
}
function findCodingQuiz() {
const quizzes = document.querySelectorAll('section.quiz_type_coding');
for (const quiz of quizzes) {
if (handledQuizzes.has(quiz)) continue;
if (quiz.classList.contains('quiz_answered')) { handledQuizzes.add(quiz); continue; }
const runBtn = quiz.querySelector('button.quiz__coding-footer-action-button');
if (!runBtn) { handledQuizzes.add(quiz); continue; }
if (quiz.offsetParent !== null) return quiz;
}
return null;
}
async function handleCodingQuiz(quiz) {
console.log('💻 Квиз с кодом — запускаем...');
const runBtn = quiz.querySelector('button.quiz__coding-footer-action-button');
runBtn.click();
// Ждём пока квиз пометится как решённый
const startTime = Date.now();
while (Date.now() - startTime < 60000) {
await sleep(CHECK_INTERVAL);
if (quiz.classList.contains('quiz_answered') || !quiz.querySelector('button.quiz__coding-footer-action-button')) {
console.log('✓ Код выполнен');
break;
}
}
handledQuizzes.add(quiz);
await sleep(CLICK_DELAY);
return true;
}
function findQuizForm() {
const forms = document.querySelectorAll(QUIZ_FORM_SELECTOR);
for (const form of forms) {
if (handledQuizzes.has(form)) continue;
if (isQuizAnswered(form)) {
handledQuizzes.add(form);
continue;
}
if (form.offsetParent !== null) {
return form;
}
}
return null;
}
// Симуляция нативного ввода текста
function simulateTyping(textarea, text) {
textarea.focus();
// Устанавливаем значение через нативный setter
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype, 'value'
).set;
nativeInputValueSetter.call(textarea, text);
// Триггерим все возможные события
textarea.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
textarea.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true }));
textarea.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
textarea.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true }));
// React-specific события
const reactEvent = new Event('input', { bubbles: true });
reactEvent.simulated = true;
textarea.dispatchEvent(reactEvent);
}
function isLastFeedbackPage(form) {
const counter = form.querySelector('.quiz__counter');
if (!counter) return true;
const parts = counter.textContent.split('—').map(s => s.trim());
return parts.length === 2 && parts[0] === parts[1];
}
async function handleFeedbackPage(form) {
const textarea = form.querySelector(FEEDBACK_TEXTAREA_SELECTOR);
if (textarea) {
simulateTyping(textarea, 'СПАСИБО!');
console.log('✓ Введён текст "СПАСИБО!"');
await sleep(500);
let nextBtn = form.querySelector(FEEDBACK_NEXT_SELECTOR);
if (nextBtn && nextBtn.disabled) {
const reactKey = Object.keys(textarea).find(key => key.startsWith('__reactFiber') || key.startsWith('__reactProps'));
if (reactKey) {
const reactProps = textarea[reactKey];
if (reactProps && reactProps.onChange) {
reactProps.onChange({ target: textarea });
}
}
await sleep(300);
}
} else {
const radioGroups = form.querySelectorAll('.likert-scale__statement-row');
for (const row of radioGroups) {
const firstRadio = row.querySelector('input[type="radio"]');
if (firstRadio && !firstRadio.checked) {
firstRadio.click();
await sleep(100);
}
}
console.log('✓ Выбраны ответы "да"');
}
await sleep(300);
let nextBtn = form.querySelector(FEEDBACK_NEXT_SELECTOR);
let attempts = 0;
while (nextBtn && nextBtn.disabled && attempts < 15) {
await sleep(200);
nextBtn = form.querySelector(FEEDBACK_NEXT_SELECTOR);
attempts++;
}
if (nextBtn && nextBtn.disabled) {
console.log('⚠️ Кнопка disabled, пробуем принудительный клик...');
nextBtn.disabled = false;
nextBtn.classList.remove('button2_disabled');
}
if (nextBtn) {
nextBtn.click();
console.log('✓ Нажата кнопка "Далее"');
await sleep(CLICK_DELAY);
return true;
}
return false;
}
async function handleFeedbackForm(form) {
console.log('📝 Форма обратной связи...');
while (true) {
const last = isLastFeedbackPage(form);
const counter = form.querySelector('.quiz__counter');
console.log(`📄 Страница: ${counter ? counter.textContent.trim() : '?'}`);
await handleFeedbackPage(form);
if (last) break;
// Ждём загрузки следующей страницы формы
await sleep(CLICK_DELAY);
}
handledFeedbacks.add(form);
return true;
}
async function handleQuizForm(form) {
console.log('❓ Квиз...');
const checkboxes = form.querySelectorAll('input[type="checkbox"]');
if (checkboxes.length > 0) {
for (const cb of checkboxes) {
if (!cb.checked) cb.click();
await sleep(100);
}
console.log(`✓ Выбраны все чекбоксы (${checkboxes.length})`);
} else {
const firstRadio = form.querySelector('input[type="radio"]');
if (firstRadio) {
firstRadio.click();
console.log('✓ Выбран первый вариант');
await sleep(300);
}
}
const submitBtn = form.querySelector(QUIZ_SUBMIT_SELECTOR);
if (submitBtn && !submitBtn.disabled) {
submitBtn.click();
console.log('✓ Нажата кнопка "Узнать ответ"');
handledQuizzes.add(form);
await sleep(CLICK_DELAY);
return true;
}
await sleep(200);
const submitBtnRetry = form.querySelector(QUIZ_SUBMIT_SELECTOR);
if (submitBtnRetry && !submitBtnRetry.disabled) {
submitBtnRetry.click();
console.log('✓ Нажата кнопка "Узнать ответ"');
handledQuizzes.add(form);
await sleep(CLICK_DELAY);
return true;
}
handledQuizzes.add(form);
return false;
}
function scrollToElement(element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function waitForElement(timeout = MAX_WAIT_TIME) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (!isRunning) return null;
const codingQuiz = findCodingQuiz();
if (codingQuiz) return { type: 'coding', element: codingQuiz };
const textQuiz = findTextQuiz();
if (textQuiz) return { type: 'text', element: textQuiz };
const quiz = findQuizForm();
if (quiz) return { type: 'quiz', element: quiz };
const feedback = findFeedbackForm();
if (feedback) return { type: 'feedback', element: feedback };
const nextLesson = findNextLessonButton();
if (nextLesson) return { type: 'next', button: nextLesson };
const button = findNewButton();
if (button) return { type: 'expander', button: button };
await sleep(CHECK_INTERVAL);
}
return null;
}
async function processLesson() {
while (isRunning) {
let codingQuiz = findCodingQuiz();
if (codingQuiz) {
scrollToElement(codingQuiz);
await sleep(300);
await handleCodingQuiz(codingQuiz);
continue;
}
let textQuiz = findTextQuiz();
if (textQuiz) {
scrollToElement(textQuiz);
await sleep(300);
await handleTextQuiz(textQuiz);
continue;
}
let quiz = findQuizForm();
if (quiz) {
scrollToElement(quiz);
await sleep(300);
await handleQuizForm(quiz);
continue;
}
let feedback = findFeedbackForm();
if (feedback) {
scrollToElement(feedback);
await sleep(300);
await handleFeedbackForm(feedback);
continue;
}
let nextLesson = findNextLessonButton();
if (nextLesson) {
return nextLesson;
}
let button = findNewButton();
if (!button) {
console.log('⏳ Ожидание...');
const result = await waitForElement();
if (!result) {
console.log('🏁 Элементов больше нет');
return null;
}
if (result.type === 'coding') {
scrollToElement(result.element);
await sleep(300);
await handleCodingQuiz(result.element);
continue;
}
if (result.type === 'text') {
scrollToElement(result.element);
await sleep(300);
await handleTextQuiz(result.element);
continue;
}
if (result.type === 'quiz') {
scrollToElement(result.element);
await sleep(300);
await handleQuizForm(result.element);
continue;
}
if (result.type === 'feedback') {
scrollToElement(result.element);
await sleep(300);
await handleFeedbackForm(result.element);
continue;
}
if (result.type === 'next') {
return result.button;
}
button = result.button;
}
scrollToElement(button);
await sleep(300);
clickedButtons.add(button);
const buttonText = button.textContent.trim();
button.click();
clickCount++;
console.log(`✓ [${clickCount}] Нажата: "${buttonText}"`);
await sleep(CLICK_DELAY);
}
return null;
}
async function run() {
console.log('🚀 Запуск...');
while (isRunning) {
lessonCount++;
console.log(`📖 Урок ${lessonCount}`);
const nextLessonBtn = await processLesson();
if (!nextLessonBtn) {
console.log('🏁 Уроки закончились');
break;
}
scrollToElement(nextLessonBtn);
await sleep(300);
nextLessonBtn.click();
console.log('➡️ Переход к следующему уроку...');
await sleep(PAGE_LOAD_DELAY);
// Проверяем не завис ли загрузчик
const loadingStart = Date.now();
while (document.querySelector('.page__loading')) {
await sleep(1000);
const elapsed = Date.now() - loadingStart;
if (elapsed > 60000) {
isRunning = false;
alert('⚠️ Страница грузится больше минуты. Обновите страницу и запустите скрипт снова.');
return;
}
}
clickedButtons = new WeakSet();
handledQuizzes = new WeakSet();
handledFeedbacks = new WeakSet();
}
console.log(`\n✅ Готово!`);
console.log(`📊 Уроков пройдено: ${lessonCount}`);
console.log(`🖱️ Кнопок нажато: ${clickCount}`);
}
function handleAssociatedProgramsPage() {
const cards = document.querySelectorAll('button.card.course-card');
for (const card of cards) {
const isCompleted = card.querySelector('.course-card__info-item_active');
if (!isCompleted) {
const title = card.querySelector('.course-card__title')?.textContent?.trim() || '(без названия)';
console.log(`🎯 Найдена непройденная программа: "${title}"`);
card.click();
return true;
}
}
console.log('✅ Все программы пройдены');
return false;
}
async function waitForModal() {
return new Promise(resolve => {
const startTime = Date.now();
const interval = setInterval(() => {
const el = document.querySelector('#portals .skills-modal_visible');
if (el) { clearInterval(interval); resolve(el); }
else if (Date.now() - startTime > MAX_WAIT_TIME) { clearInterval(interval); resolve(null); }
}, CHECK_INTERVAL);
});
}
async function handleCourseModal() {
console.log('🔍 Ищем непройденную тему в модалке...');
const modal = await waitForModal();
if (!modal) { console.log('⚠️ Модальное окно не появилось'); return false; }
// Шаг 1.js: найти и кликнуть первую тему без course-navigation-topic_solved
const topics = modal.querySelectorAll('a.course-navigation-topic');
let clickedTopic = false;
for (const topic of topics) {
if (!topic.classList.contains('course-navigation-topic_solved')) {
const title = topic.querySelector('.course-navigation-item__title')?.textContent?.trim() || '(без названия)';
console.log(`📂 Тема: "${title}"`);
topic.click();
clickedTopic = true;
break;
}
}
if (!clickedTopic) { console.log('✅ Все темы пройдены'); return false; }
// Шаг 2: ждём пока появятся уроки внутри темы
await sleep(CLICK_DELAY);
const startTime = Date.now();
let lesson = null;
while (Date.now() - startTime < MAX_WAIT_TIME) {
const lessons = modal.querySelectorAll('a.course-navigation-lesson');
for (const l of lessons) {
if (!l.classList.contains('course-navigation-lesson_status_solved')) {
lesson = l;
break;
}
}
if (lesson) break;
await sleep(CHECK_INTERVAL);
}
if (!lesson) { console.log('⚠️ Непройденный урок не найден'); return false; }
const lessonTitle = lesson.querySelector('.course-navigation-item__title')?.textContent?.trim() || '(без названия)';
console.log(`📚 Урок: "${lessonTitle}"`);
lesson.click();
return true;
}
function insertSolutionIntoMonaco(code) {
const editors = window.monaco.editor.getEditors();
if (!editors || editors.length === 0) {
console.log('⚠️ Monaco редактор не найден');
return false;
}
const editor = editors[0];
const model = editor.getModel();
if (!model) {
console.log('⚠️ Monaco модель не найдена');
return false;
}
const fullRange = model.getFullModelRange();
editor.executeEdits('auto-solution', [{
range: fullRange,
text: code,
forceMoveMarkers: true,
}]);
editor.focus();
console.log('✅ Решение вставлено в редактор');
return true;
}
async function fetchAnswerPanes() {
const html = await fetch(window.location.href).then(r => r.text());
const match = html.match(/"answer_panes":(\[.*?\]),"has_author_solution"/);
if (!match) return null;
try {
return JSON.parse(match[1]);
} catch (e) {
return null;
}
}
async function handleTrainerPage() {
if (!document.querySelector('button.trainer-footer__solution-button')) {
console.log('💡 Загружаем исходник страницы для поиска решения...');
const answerPanes = await fetchAnswerPanes();
if (!answerPanes || answerPanes.length === 0) {
console.log(' Авторского решения нет для этой задачи');
return false;
}
const solution = answerPanes[0].content;
if (!solution) {
console.log(' Контент решения пустой');
return false;
}
console.log(`💡 Найдено решение (${answerPanes[0].name}), вставляем...`);
insertSolutionIntoMonaco(solution);
await sleep(CLICK_DELAY);
const checkBtn = document.querySelector('button.trainer-footer__check-button');
if (checkBtn) {
checkBtn.click();
console.log('✅ Нажата кнопка "Проверить", ждём результата...');
} else {
console.log('⚠️ Кнопка "Проверить" не найдена');
return true;
}
} else {
console.log(' Задача уже решена');
}
// Ждём кнопку "Далее" без ограничения по времени
let nextBtn = null;
let elapsed = 0;
while (!nextBtn) {
await sleep(CHECK_INTERVAL);
elapsed += CHECK_INTERVAL;
nextBtn = document.querySelector('button.trainer-footer__solution-button');
if (elapsed % 10000 === 0) {
console.log(`⏳ Ожидаем результат проверки... (${elapsed / 1000}с)`);
}
}
nextBtn.click();
console.log('➡️ Нажата кнопка "Далее"');
return true;
}
async function dispatch() {
// Сброс состояния при каждом вызове
isRunning = true;
clickedButtons = new WeakSet();
handledQuizzes = new WeakSet();
handledFeedbacks = new WeakSet();
const path = window.location.pathname;
console.log(`🔄 Страница: ${path}`);
if (path.includes('/profile/associated-programs-android-st-v2')) {
const found = handleAssociatedProgramsPage();
if (found) await handleCourseModal();
} else if (path.includes('/trainer/') && window.monaco?.editor.getEditors().length > 0) {
await handleTrainerPage();
} else if (path.includes('/trainer/') || path.includes('/topics/')) {
await run();
}
}
// Перехват SPA-навигации
const _pushState = history.pushState.bind(history);
history.pushState = function(...args) {
_pushState(...args);
setTimeout(() => dispatch(), PAGE_LOAD_DELAY);
};
window.addEventListener('popstate', () => {
setTimeout(() => dispatch(), PAGE_LOAD_DELAY);
});
window.stopAutoClick = function() {
isRunning = false;
console.log('⏹ Остановлено');
};
console.log('💡 Для остановки: stopAutoClick()');
await dispatch();
})();