Фиксы: автозакрытие браузера после отправки, README, вторая модалка подтверждения hh.ru #2
No files matched your search
@@ -1,9 +1,9 @@
|
||||
# Автоотклик на hh.ru (полуавтоматический, безопасный)
|
||||
# Автоотклик на hh.ru (полуавтоматический)
|
||||
|
||||
Скрипт ищет вакансии на hh.ru по ключевым словам, генерирует персонализированный
|
||||
текст отклика через ИИ (на основе вашего резюме и описания вакансии), открывает
|
||||
страницу в браузере и подставляет текст. **Кнопку «Отправить» всегда нажимаете вы** —
|
||||
это не нарушает правила hh.ru и не рискует аккаунтом.
|
||||
страницу в браузере, подставляет текст и резюме. Отправка — в один клик
|
||||
(«Отправить отклик»), либо с проверкой текста перед отправкой.
|
||||
|
||||
## Установка
|
||||
|
||||
@@ -25,17 +25,30 @@ PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright .venv/bin
|
||||
.venv/bin/python webui.py
|
||||
```
|
||||
|
||||
Откройте **http://localhost:5000** — там можно:
|
||||
Откройте **http://localhost:18091** (порт меняется через `PORT`) — там можно:
|
||||
|
||||
- регистрироваться и входить (открытая регистрация, отключается `ALLOW_REGISTRATION=0`)
|
||||
- создавать аккаунты (имя, регион, поисковые запросы, стиль)
|
||||
- загружать резюме файлом (**txt, md, docx, pdf**)
|
||||
- сохранять **ключ сессии hh.ru** (cookies из браузера) — нужен для поиска и списка резюме
|
||||
- выбирать **резюме на hh.ru** — оно автоматически подставляется в модалке отклика
|
||||
- искать вакансии и видеть их списком (название, компания, зарплата, опыт)
|
||||
- генерировать тексты откликов через ИИ (редактируются перед отправкой)
|
||||
- отправлять отклик в один клик: кнопка «Отправить отклик» в колонке действий —
|
||||
браузер открывает вакансию, вставляет текст, жмёт «Отправить» и закрывается
|
||||
(скриншот результата показывается в модалке)
|
||||
- открывать вакансию в браузере с уже вставленным текстом и выбранным резюме
|
||||
для ручной проверки перед отправкой
|
||||
- отмечать отправленные — они больше не предлагаются
|
||||
|
||||
### Регистрация
|
||||
|
||||
При первом запуске откройте `/register` и создайте логин с паролем (минимум
|
||||
6 символов) — вы сразу войдёте. Каждый пользователь работает со своими
|
||||
аккаунтами и лимитом генераций. Открытая регистрация отключается
|
||||
`ALLOW_REGISTRATION=0` (пользователей тогда создаёт администратор через
|
||||
`manage_users.py`).
|
||||
|
||||
### Ключ сессии hh.ru
|
||||
|
||||
Чтобы скрипт мог искать вакансии и видеть ваши резюме, нужны cookies из браузера:
|
||||
@@ -89,13 +102,30 @@ accounts/
|
||||
1. Скрипт парсит страницу поиска hh.ru по вашим запросам
|
||||
2. Для каждой новой вакансии получает описание
|
||||
3. ИИ генерирует текст отклика (вакансия + ваше резюме + стиль)
|
||||
4. Браузер открывает вакансию, жмёт «Откликнуться», вставляет текст
|
||||
5. **Вы проверяете текст и нажимаете «Отправить»**
|
||||
6. Отмечаете отправленное — скрипт переходит к следующей
|
||||
4. **Один клик**: кнопка «Отправить отклик» — браузер открывает вакансию,
|
||||
жмёт «Откликнуться», вставляет текст, выбирает резюме, жмёт «Отправить»
|
||||
и автоматически закрывается (скриншот результата — в модалке)
|
||||
5. **С проверкой**: «Открыть в браузере» — вы проверяете текст и отправляете
|
||||
сами, либо жмёте «Отправить отклик» в модалке скриншота
|
||||
6. Отправленные вакансии отмечаются и больше не предлагаются
|
||||
|
||||
## Переменные окружения (.env)
|
||||
|
||||
| Переменная | По умолчанию | Описание |
|
||||
|---|---|---|
|
||||
| `LLM_API_KEY` | — | Ключ API для генерации текстов откликов |
|
||||
| `SESSION_SECRET` | `dev-secret-change-me` | Секрет сессий Flask (обязательно сменить) |
|
||||
| `PORT` | `18091` | Порт веб-интерфейса |
|
||||
| `HOST` | `127.0.0.1` | Адрес прослушивания |
|
||||
| `ALLOW_REGISTRATION` | `1` | Открытая регистрация (`0` — выключить) |
|
||||
| `REGISTER_LLM_LIMIT` | `50` | Лимит генераций для новых пользователей |
|
||||
| `HH_HEADLESS` | `0` | `1` — браузер без окна (для сервера без дисплея) |
|
||||
| `PROXY_URL` | — | Прокси для браузера: `http://LOGIN:PASS@HOST:PORT` |
|
||||
|
||||
## Важно
|
||||
|
||||
- Скрипт не нарушает правила hh.ru: финальное действие всегда за человеком
|
||||
- Отправка в один клик автоматически жмёт «Отправить» — используйте с осторожностью
|
||||
и не чаще, чем позволяет hh.ru (паузы между страницами уже стоят)
|
||||
- Не запускайте поиск слишком часто — между страницами есть паузы
|
||||
- Если hh.ru покажет капчу — скрипт остановится, решите капчу вручную и запустите снова
|
||||
- `.env` содержит API-ключ — не коммитьте его в git (уже добавлен в `.gitignore`)
|
||||
+56
-8
@@ -37,6 +37,15 @@ SUBMIT_BUTTONS = [
|
||||
'[data-qa="vacancy-response-popup-form"] button[type="submit"]',
|
||||
'button:has-text("Отправить")',
|
||||
]
|
||||
# Кнопки подтверждения во ВТОРОЙ модалке hh.ru: после клика «Отправить»
|
||||
# иногда появляется модалка «Отклик будет отправлен» с кнопкой
|
||||
# «Отправить отклик» — её нужно нажать ещё раз.
|
||||
CONFIRM_BUTTONS = [
|
||||
'button:has-text("Отправить отклик")',
|
||||
'button:has-text("Подтвердить")',
|
||||
'button:has-text("Да, отправить")',
|
||||
'[data-qa="vacancy-response-confirm"]',
|
||||
]
|
||||
|
||||
# Живые браузерные контексты и страницы: {(user, account): ...}
|
||||
# Доступны ТОЛЬКО из браузерного потока.
|
||||
@@ -199,6 +208,26 @@ def _save_screenshot(page, screenshot_dir, vid):
|
||||
return None
|
||||
|
||||
|
||||
def _click_second_confirm(page):
|
||||
"""После клика «Отправить» hh.ru иногда показывает вторую модалку
|
||||
подтверждения («Отклик будет отправлен») — жмём и её.
|
||||
Возвращает True, если вторая кнопка найдена и нажата."""
|
||||
try:
|
||||
page.wait_for_timeout(1500)
|
||||
except Exception:
|
||||
return False
|
||||
for sel in CONFIRM_BUTTONS:
|
||||
try:
|
||||
btn = page.locator(sel).first
|
||||
if btn.is_visible(timeout=1500):
|
||||
btn.click()
|
||||
print(" ✓ Вторая модалка подтверждения — кнопка нажата")
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def open_response_page(account_dir, user, account, vacancy, message,
|
||||
resume_id=None, screenshot_dir=None, submit=False):
|
||||
"""Открывает вакансию, жмёт «Откликнуться», выбирает резюме, вставляет текст.
|
||||
@@ -277,11 +306,21 @@ def open_response_page(account_dir, user, account, vacancy, message,
|
||||
if not submitted:
|
||||
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
|
||||
return False, "Не нашёл кнопку «Отправить» — проверьте скриншот", shot
|
||||
# Ждём подтверждение и снимаем скриншот результата
|
||||
page.wait_for_timeout(2500)
|
||||
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
|
||||
# Отклик отправлен — закрываем браузер автоматически
|
||||
_close_context(key)
|
||||
# hh.ru иногда показывает вторую модалку подтверждения — жмём и её
|
||||
try:
|
||||
_click_second_confirm(page)
|
||||
except Exception:
|
||||
pass
|
||||
# Ждём подтверждение и снимаем скриншот результата.
|
||||
# После отправки hh.ru может закрыть модалку/перезагрузить страницу —
|
||||
# ловим любые ошибки, но браузер закрываем ВСЕГДА (finally).
|
||||
try:
|
||||
page.wait_for_timeout(2500)
|
||||
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
|
||||
except Exception:
|
||||
shot = None
|
||||
finally:
|
||||
_close_context(key)
|
||||
return True, "Отклик отправлен", shot
|
||||
|
||||
shot = _save_screenshot(page, screenshot_dir, vacancy["id"])
|
||||
@@ -299,9 +338,18 @@ def confirm_send(user, account, vid):
|
||||
btn = page.locator(sel).first
|
||||
if btn.is_visible(timeout=3000):
|
||||
btn.click()
|
||||
page.wait_for_timeout(1500)
|
||||
# Отклик отправлен — закрываем браузер автоматически
|
||||
_close_context(key)
|
||||
try:
|
||||
# Вторая модалка подтверждения (если появилась)
|
||||
_click_second_confirm(page)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
page.wait_for_timeout(1500)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
# Отклик отправлен — закрываем браузер автоматически
|
||||
_close_context(key)
|
||||
return True, "Кнопка «Отправить» нажата. Проверьте статус отклика на hh.ru."
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user