From d757d2f8c7dc27a686c659d06709b2bd76c2263b Mon Sep 17 00:00:00 2001 From: apuc Date: Tue, 22 Sep 2026 11:11:13 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20hh.ru=20=D0=BF=D1=80=D1=8F=D1=87=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BF=D0=BE=D0=BB=D0=B5=20=D0=BF=D0=B8=D1=81=D1=8C?= =?UTF-8?q?=D0=BC=D0=B0=20=D0=B7=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D0=BB=D1=8E=D1=87=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=BC=20?= =?UTF-8?q?=E2=80=94=20=D0=BA=D0=BB=D0=B8=D0=BA=20=D0=BF=D0=BE=20vacancy-r?= =?UTF-8?q?esponse-letter-toggle,=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=81=D0=B5=D0=BB=D0=B5=D0=BA=D1=82=D0=BE=D1=80=20=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D1=8C=D0=BC=D0=B0=20=D0=B8=20=D0=BA=D0=BD=D0=BE=D0=BF?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= =?UTF-8?q?;=20=D1=84=D1=80=D0=BE=D0=BD=D1=82=D0=B5=D0=BD=D0=B4=20=D0=B1?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BD=D0=B5=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20=D1=83=D1=81?= =?UTF-8?q?=D0=BF=D0=B5=D1=85=20=D0=BF=D1=80=D0=B8=20ok=3Dfalse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hh_browser.py | 16 ++++++++++++++++ templates/index.html | 10 ++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hh_browser.py b/hh_browser.py index 5b3e84a..97bc855 100644 --- a/hh_browser.py +++ b/hh_browser.py @@ -27,12 +27,17 @@ RESPONSE_BUTTONS = [ '[data-qa="vacancy-response-link"]', 'button:has-text("Откликнуться")', ] +# hh.ru прячет поле письма за переключателем «Сопроводительное письмо / Добавить»: +# до клика по нему на форме есть только textarea вопроса скрининга (task_*). +LETTER_TOGGLE = '[data-qa="vacancy-response-letter-toggle"]' MESSAGE_FIELDS = [ + 'textarea[data-qa="vacancy-response-popup-form-letter-input"]', 'textarea[data-qa="vacancy-response-letter"]', 'textarea[data-qa="vacancy-response-popup-form"] textarea', 'textarea', ] SUBMIT_BUTTONS = [ + '[data-qa="vacancy-response-submit-popup"]', '[data-qa="vacancy-response-submit"]', '[data-qa="vacancy-response-popup-form"] button[type="submit"]', 'button:has-text("Отправить")', @@ -265,6 +270,17 @@ def open_response_page(account_dir, user, account, vacancy, message, # Ждём модалку page.wait_for_timeout(2000) + # hh.ru прячет поле письма за переключателем «Сопроводительное письмо» — + # раскрываем его, иначе заполнится textarea вопроса скрининга (task_*) + try: + toggle = page.locator(LETTER_TOGGLE).first + if toggle.is_visible(timeout=2000): + toggle.click() + page.wait_for_timeout(800) + print(" ✓ Переключатель «Сопроводительное письмо» раскрыт") + except Exception: + pass + # Выбор резюме (радио-кнопка name="resumeId" value="{hash}") if resume_id: try: diff --git a/templates/index.html b/templates/index.html index 8b9d47b..860f5cb 100644 --- a/templates/index.html +++ b/templates/index.html @@ -677,7 +677,7 @@ async function sendFromModal() { const data = await api(`/api/accounts/${currentAccount}/send-now`, { method: 'POST', body: JSON.stringify({ id: sendModalId, message }), }); - if (data.screenshot) { + if (data.ok && data.screenshot) { showShotModal(sendModalId, data.screenshot, data.note || 'Отклик отправлен', true); sentTexts.add(sendModalId); const v = vacancies.find(x => x.id === sendModalId); @@ -685,6 +685,10 @@ async function sendFromModal() { renderVacancies(); renderTexts(); closeSendModal(); + } else if (data.screenshot) { + // Бэкенд вернул скриншот, но отклик НЕ отправлен (например, не нашёл поле) + showShotModal(sendModalId, data.screenshot, data.note || 'Не удалось отправить отклик', false); + btn.disabled = false; } else { note.textContent = data.note || 'Не удалось отправить отклик'; btn.disabled = false; @@ -737,13 +741,15 @@ async function sendVacancy(id) { method: 'POST', body: JSON.stringify({ id, message }), }); if (note) note.textContent = data.note || ''; - if (data.screenshot) { + if (data.ok && data.screenshot) { showShotModal(id, data.screenshot, data.note || 'Отклик отправлен', true); sentTexts.add(id); const v = vacancies.find(x => x.id === id); if (v) v.responded = true; renderVacancies(); renderTexts(); + } else if (data.screenshot) { + showShotModal(id, data.screenshot, data.note || 'Не удалось отправить отклик', false); } else { alert(data.note || 'Не удалось отправить отклик'); }