Files
hh-auto-reply/README.md
Oscar 162abdfc0a
All checks were successful
Deploy / deploy (push) Successful in 29s
🔧 upd(README.md)
2026-06-05 11:33:41 +03:00

252 lines
11 KiB
Markdown
Raw 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.
# HH Auto-Apply Bot
Telegram-бот для автоматизации откликов на вакансии с **hh.ru**. Авторизуется на сайте от имени пользователя, парсит вакансии по заданному запросу и генерирует персонализированное сопроводительное письмо под каждую позицию — с учётом резюме и описания вакансии.
> **Статус:** активная разработка. Генерация писем работает; финальный клик «Откликнуться» реализован, логика отклика отлажена на реальных вакансиях.
---
## Что умеет бот
- **Авторизация через OTP** — входит на hh.ru по email или номеру телефона + одноразовый код, сессия (cookies) сохраняется в БД и переиспользуется
- **Парсинг резюме** — скачивает резюме с hh.ru после логина, сохраняет текстом в БД
- **Поиск вакансий** — по настраиваемому запросу, с лимитом откликов за сессию
- **AI-письма** — уникальное сопроводительное письмо под каждую вакансию через LLM (DeepSeek V4 Flash / OpenRouter)
- **Пропуск анкет** — вакансии с опросником автоматически пропускаются и логируются
- **Авто-режим** — cron-задача (пн–пт, 10:00) для ежедневного автозапуска
- **Полное управление через Telegram** — настройки, промпт, резюме, статус — всё в чате
---
## Стек
| Слой | Технология |
|---|---|
| Runtime | Node.js 22+ · TypeScript · ESM |
| Telegram | node-telegram-bot-api (long polling) |
| Браузерная автоматизация | Playwright (headless Chromium) |
| LLM | DeepSeek V4 Flash · OpenRouter · `@opencode-ai/sdk`; Groq (`llama-3.3-70b-versatile`) как запасной |
| ORM | Prisma 6 · SQLite |
| Планировщик | node-cron |
| Контейнеризация | Docker |
| Пакетный менеджер | Yarn 4 (PnP off) |
---
## Как это работает
```
Пользователь в Telegram
├─ /start → главное меню
├─ Войти на hh.ru
│ └─ Playwright открывает браузер, вводит email или телефон
│ └─ OTP-код пользователь присылает в чат
│ └─ Сессия (cookies) сохраняется в БД
├─ Выбрать резюме → Playwright парсит список резюме с hh.ru
└─ Откликнуться
└─ Playwright ищет вакансии по запросу
└─ Для каждой вакансии:
├─ Парсит описание (название, требования, компания)
├─ OpenCode SDK → DeepSeek V4 Flash генерирует письмо (резюме + описание → письмо)
├─ Playwright вставляет письмо и кликает «Откликнуться»
└─ Отчёт в Telegram: применено / пропущено / ошибки
```
---
## AI: как генерируются письма
Используется **DeepSeek V4 Flash** через [OpenRouter](https://openrouter.ai). Интеграция реализована через `@opencode-ai/sdk` — при первом запросе бот поднимает локальный OpenCode-сервер (`localhost:4096`), при последующих переиспользует его.
Каждая сессия генерации состоит из двух шагов:
1. **Первый промпт (без ответа)** — системная инструкция + полный текст резюме
2. **Второй промпт** — описание вакансии, модель возвращает готовое письмо
Пользователь настраивает системный промпт прямо в боте через кнопку `📝 Промт`. По умолчанию модель пишет коротко, опирается только на факты из резюме и добавляет контакты в конце.
---
## Архитектура
Одиночный Node.js процесс без HTTP-сервера. Весь UI — Telegram reply-клавиатуры и inline-кнопки.
```
src/
├── index.ts # точка входа, регистрация хендлеров
├── bot-singleton.ts # singleton TelegramBot (@bot)
├── prisma.ts # singleton PrismaClient (@prisma)
├── openai.ts # LLM: OpenCode SDK (DeepSeek V4 Flash/OpenRouter) + Groq fallback
└── hh/
├── bot-commands.ts # маршрутизация: handler maps вместо switch
├── state.ts # UserState (awaiting-флаги, cron, pending resumes)
├── scraper.ts # Playwright-автоматизация hh.ru
├── browser.ts # stealth-контекст, сессии, утилиты
├── ui.ts # клавиатуры, escapeHtml, StatusReporter
├── types.ts # общие типы
└── handlers/
├── apply.ts # запуск поиска и откликов
├── auth.ts # логин (email / телефон), OTP-флоу
├── info.ts # статус, проблемные вакансии
├── onboarding.ts # онбординг нового пользователя
├── resume.ts # список резюме, просмотр, выбор
├── settings.ts # запрос, лимит, промпт, авто-режим
└── debug.ts # отладочные функции
```
**Path-алиасы** (tsconfig.json): `@bot`, `@prisma`, `@/*``src/*`
Состояние диалога (`awaitingEmail`, `awaitingQuery` и т.д.) хранится in-memory в `Map<chatId, UserState>` — сбрасывается при рестарте процесса. Персистентные данные (сессия, резюме, настройки) — только в БД.
---
## База данных
```prisma
model User {
telegramId BigInt @unique
username String?
firstName String?
hhEmail String?
hhPhone String?
session String? // cookies hh.ru (JSON)
prompt String // системный промпт для AI
resumes Resume[]
Settings Settings?
}
model Resume {
id String @id // хэш от URL резюме на hh.ru
title String
data String // полный текст резюме
telegramId BigInt
}
model Settings {
telegramId BigInt @id
searchQuery String @default("Vue")
maxApplies Int @default(1)
selectedResumeId String?
}
model SkippedVacancy {
telegramId BigInt
href String
title String
createdAt DateTime
@@unique([telegramId, href])
}
```
---
## Запуск локально
### 1. Зависимости
```bash
yarn
```
### 2. Переменные окружения
Создай `.env` в корне проекта:
```env
# База данных (обязательно)
DATABASE_URL="file:./prisma/dev.db"
# Telegram Bot Token — получить у @BotFather
TG_BOT_TOKEN=1234567890:AAFxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# OpenRouter API Key — нужен для DeepSeek V4 Flash (openrouter.ai)
OPENROUTER_API_KEY=sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Groq API Key — используется функцией askGPT (запасной путь)
GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### 3. Миграции и запуск
```bash
yarn db-migrate # создать БД и применить миграции
yarn dev # запуск с hot reload
```
---
## Команды
```bash
yarn dev # запуск с hot reload (tsx watch + .env)
yarn build # компиляция TypeScript → dist/
yarn start # запуск скомпилированного dist/index.js
yarn lint # ESLint проверка
yarn lint:fix # ESLint автофикс
yarn db-view # Prisma Studio — GUI для БД
yarn db-migrate # создать и применить миграцию (dev)
yarn db:deploy # применить миграции без создания новых (prod)
```
---
## Docker
### Сборка
```bash
docker build -t hh-auto-apply-bot .
```
### Запуск
Создай `.env.docker`:
```env
DATABASE_URL=file:/data/dev.db
TG_BOT_TOKEN=...
OPENROUTER_API_KEY=sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
```bash
# Linux / macOS
docker run --rm --env-file .env.docker -v $(pwd)/data:/data hh-auto-apply-bot
# Windows PowerShell
docker run --rm --env-file .env.docker -v ${PWD}/data:/data hh-auto-apply-bot
```
SQLite-база сохраняется в `./data/` и переживает перезапуски контейнера.
---
## UI бота
**Главное меню**
| Кнопка | Действие |
|---|---|
| `🚀 Откликнуться` | Запустить поиск и отклики |
| `⚙️ Настройки` | Открыть меню настроек |
| ` Информация` | Открыть меню информации |
**Настройки**
| Кнопка | Действие |
|---|---|
| `🔢 Макс. откликов` | Лимит откликов за сессию (150) |
| `🔍 Изменить запрос` | Поисковый запрос на hh.ru |
| `⏰ Авто` | Вкл/выкл ежедневный cron (пн–пт 10:00) |
| `📄 Выбрать резюме` | Загрузить список резюме с hh.ru |
| `📝 Промт` | Настроить системный промпт для AI |
| `🔑 Войти на hh.ru` | Авторизация (email или телефон + OTP) |
**Информация**
| Кнопка | Действие |
|---|---|
| `⚙️ Статус` | Текущие настройки и статус авторизации |
| `📋 Моё резюме` | Просмотр сохранённого резюме |
| `🚫 Проблемные вакансии` | Вакансии с анкетой (бот не может откликнуться) |