fix: письмо больше не попадает в вопрос скрининга — убран опасный fallback 'textarea' при наличии task_*, надёжный клик по переключателю письма, проверка ошибки валидации после отправки

This commit is contained in:
apuc committed 2026-09-22 12:43:04 +03:00
1 parent 6b2c7a0f09
commit 3179bac026
1 file changed
+36 -7
+36 -7
View File
@@ -29,12 +29,13 @@ RESPONSE_BUTTONS = [
]
# hh.ru прячет поле письма за переключателем «Сопроводительное письмо / Добавить»:
# до клика по нему на форме есть только textarea вопроса скрининга (task_*).
# ВАЖНО: запасной селектор 'textarea' НЕ используется, если есть вопросы скрининга —
# иначе текст письма попадёт в первый вопрос вместо поля письма.
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"]',
@@ -272,18 +273,18 @@ def open_response_page(account_dir, user, account, vacancy, message,
return False, "Не нашёл кнопку «Откликнуться» — проверьте скриншот", shot
# Ждём модалку
page.wait_for_timeout(2000)
page.wait_for_timeout(2500)
# 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(" ✓ Переключатель «Сопроводительное письмо» раскрыт")
toggle.wait_for(state="visible", timeout=5000)
toggle.click()
page.wait_for_timeout(800)
print(" ✓ Переключатель «Сопроводительное письмо» раскрыт")
except Exception:
pass
print(" ⚠ Переключатель письма не найден — поле может отсутствовать")
# Выбор резюме (радио-кнопка name="resumeId" value="{hash}")
if resume_id:
@@ -307,6 +308,19 @@ def open_response_page(account_dir, user, account, vacancy, message,
except Exception:
continue
if not filled:
# Запасной вариант: если вопросов скрининга НЕТ, единственная
# textarea на форме — это поле письма (старая версия формы).
# При наличии task_* НЕ заполняем — иначе письмо попадёт в вопрос.
try:
if page.locator('textarea[name^="task_"]').count() == 0:
field = page.locator('textarea').first
if field.is_visible(timeout=2000):
field.fill(message)
filled = True
except Exception:
pass
if not filled:
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
return False, "Не нашёл поле сообщения — проверьте скриншот", shot
@@ -336,7 +350,22 @@ def open_response_page(account_dir, user, account, vacancy, message,
# ловим любые ошибки, но отклик уже отправлен.
try:
page.wait_for_timeout(2500)
# Проверяем, не осталась ли форма открытой с ошибкой валидации
# (например, не заполнены обязательные вопросы скрининга)
form_open = page.locator('[data-qa="vacancy-response-popup-form"]').count()
err_visible = False
if form_open:
for sel in ['[data-qa="vacancy-response-error"]',
'[data-qa="vacancy-response-popup-form"] [class*="error"]']:
try:
if page.locator(sel).first.is_visible(timeout=1000):
err_visible = True
break
except Exception:
continue
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
if form_open and err_visible:
return False, "hh.ru не принял отклик — проверьте скриншот (возможно, не заполнены обязательные вопросы)", shot
except Exception:
shot = None
return True, "Отклик отправлен", shot