feat(file/topic): добавлено меню в главном окне бота. UI обновлен в соответствии с новыми требованиями.
All checks were successful
Deploy / deploy (push) Successful in 46s

This commit is contained in:
Oscar
2026-05-29 12:51:04 +03:00
parent 810952a5c7
commit 6acc8d0adb
8 changed files with 701 additions and 590 deletions

340
README.md
View File

@@ -1,144 +1,246 @@
# 🤖 HH Auto-Apply Bot
# HH Auto-Apply Bot
Telegram-бот для автоматизации откликов на вакансии с hh.ru. Бот логинится на hh.ru от твоего имени, парсит вакансии по запросу и с помощью LLM генерирует персонализированное сопроводительное письмо под каждую вакансию — с учётом твоего резюме и описания позиции.
Telegram-бот для автоматизации откликов на вакансии с **hh.ru**. Авторизуется на сайте от имени пользователя, парсит вакансии по заданному запросу и генерирует персонализированное сопроводительное письмо под каждую позицию — с учётом резюме и описания вакансии.
> **Статус:** активная разработка. Генерация писем работает; финальный клик «Откликнуться» реализован, логика отклика отлажена на реальных вакансиях.
---
## Что умеет бот
- **Авторизация через OTP** — входит на hh.ru по email + одноразовый код, сессия (cookies) сохраняется в БД и переиспользуется
- **Парсинг резюме** — скачивает резюме с hh.ru после логина, сохраняет текстом в БД
- **Поиск вакансий** — по настраиваемому запросу, с лимитом откликов за сессию
- **AI-письма** — уникальное сопроводительное письмо под каждую вакансию через LLM (Llama 3.3 70B / Groq)
- **Пропуск анкет** — вакансии с опросником автоматически пропускаются и логируются
- **Авто-режим** — cron-задача (пн–пт, 10:00) для ежедневного автозапуска
- **Полное управление через Telegram** — настройки, промпт, резюме, статус — всё в чате
---
## Стек
| Слой | Технология |
|---|---|
| Runtime | Node.js 20+ (ESM), TypeScript |
| Runtime | Node.js 22+ · TypeScript · ESM |
| Telegram | node-telegram-bot-api (long polling) |
| Браузерная автоматизация | Playwright (headless Chromium) |
| LLM / AI | Groq API — `llama-3.3-70b-versatile` |
| ORM | Prisma 6 + SQLite |
| LLM | DeepSeek V4 Flash · OpenRouter · через `@opencode-ai/sdk` |
| ORM | Prisma 6 · SQLite |
| Планировщик | node-cron |
| Пакетный менеджер | Yarn 4 |
| Контейнеризация | Docker |
| Пакетный менеджер | Yarn 4 (PnP off) |
---
## Как это работает
```
Пользователь Telegram Bot
├─ Авторизация на hh.ru (Playwright + OTP через бот)
│ └─ Сохранение сессии (cookies) в БД
├─ Парсинг резюме с hh.ru → сохранение в БД
└─ Поиск вакансий по запросу
└─ Для каждой вакансии:
├─ Playwright парсит описание
└─ LLM (Llama 3.3 70B) генерирует
сопроводительное письмо на основе
резюме + описания вакансии
Пользователь в Telegram
├─ /start → главное меню
├─ Войти на hh.ru
└─ Playwright открывает браузер, вводит email
└─ OTP-код пользователь присылает в чат
└─ Сессия (cookies) сохраняется в БД
├─ Выбрать резюме → Playwright парсит список резюме с hh.ru
└─ Откликнуться
└─ Playwright ищет вакансии по запросу
└─ Для каждой вакансии:
├─ Парсит описание (название, требования, компания)
├─ Groq API генерирует письмо (резюме + описание → письмо)
├─ Playwright вставляет письмо и кликает «Откликнуться»
└─ Отчёт в Telegram: применено / пропущено / ошибки
```
## AI / LLM
---
Сопроводительные письма генерирует **Llama 3.3 70B** через Groq API (OpenAI-совместимый интерфейс). Модель получает:
## AI: как генерируются письма
- Системный промпт с инструкциями по стилю (настраивается пользователем)
- Текст резюме пользователя (парсится с hh.ru)
- Описание конкретной вакансии (парсится Playwright'ом в реальном времени)
Используется **DeepSeek V4 Flash** через [OpenRouter](https://openrouter.ai). Интеграция реализована через `@opencode-ai/sdk` — при первом запросе бот поднимает локальный OpenCode-сервер (`localhost:4096`), при последующих переиспользует его.
На выходе — живое, не шаблонное письмо, адаптированное под конкретную позицию.
Каждая сессия генерации состоит из двух шагов:
В коде также подключён **Anthropic SDK** (`@anthropic-ai/sdk`) для возможности использования Claude.
1. **Первый промпт (без ответа)** — системная инструкция + полный текст резюме
2. **Второй промпт** — описание вакансии, модель возвращает готовое письмо
## Возможности
Пользователь настраивает системный промпт прямо в боте через кнопку `📝 Промт`. По умолчанию модель пишет коротко, опирается только на факты из резюме и добавляет контакты в конце.
- **Авторизация** — вход на hh.ru через email + OTP-код, сессия сохраняется в БД и переиспользуется
- **Парсинг резюме** — автоматически скачивает твоё резюме с hh.ru после логина
- **Поиск вакансий** — поиск по настраиваемому запросу с лимитом откликов
- **AI-сопроводительные письма** — уникальное письмо под каждую вакансию через LLM
- **Авто-режим** — крон-задача (пн–пт, 10:00) для ежедневного автозапуска
- **Настройки через бот** — поисковый запрос, макс. кол-во откликов — всё меняется прямо в чате
## Команды бота
| Кнопка / команда | Действие |
|---|---|
| `/start` | Регистрация, главное меню |
| `💼 Меню` | Панель управления HH Auto-Apply |
| `🚀 Откликнуться сейчас` | Запустить поиск и генерацию писем |
| `🔍 Изменить запрос` | Сменить поисковый запрос |
| `🔢 Макс откликов` | Установить лимит вакансий за сессию |
| `⏰ Авто вкл / Авто выкл` | Включить/выключить ежедневный крон |
| `🔑 Логин` | Авторизоваться на hh.ru |
| `⚙️ Статус` | Текущие настройки и статус авторизации |
## База данных
```
User — telegramId, hhEmail, session (cookies), AI-промпт
Resume — текст резюме, привязан к пользователю
Settings — searchQuery, maxApplies
```
## Docker
### Сборка образа
```bash
docker build -t oscr-test-bot .
```
### Запуск контейнера
Создай файл `.env.docker`:
```
DATABASE_URL=file:/data/dev.db
TG_BOT_TOKEN=your_token
GROQ_API_KEY=your_key
OPENROUTER_API_KEY=your_key
```
Запуск (Linux/Mac):
```bash
docker run --rm --env-file .env.docker -v $(pwd)/data:/data oscr-test-bot
```
Запуск (Windows PowerShell):
```powershell
docker run --rm --env-file .env.docker -v //c/MyApps/oscr-test-bot/data:/data oscr-test-bot
```
SQLite база данных сохраняется в папку `data/` и переживает перезапуски контейнера.
## Запуск локально
```bash
# 1. Установить зависимости
yarn
# 2. Создать .env
echo 'DATABASE_URL="file:./dev.db"' > .env
# 3. Применить миграции
yarn db-migrate
# 4. Запустить в dev-режиме
yarn dev
```
## Команды разработки
```bash
yarn dev # запуск с hot reload (tsx watch)
yarn build # компиляция TypeScript → dist/
yarn start # запуск скомпилированного кода
yarn lint # проверка ESLint
yarn lint:fix # автофикс ESLint
yarn db-view # Prisma Studio (GUI для БД)
yarn db-migrate # создать и применить миграцию
yarn db-deploy # применить миграции на проде
```
---
## Архитектура
Одиночный Node.js процесс без HTTP-сервера. Весь UI — Telegram inline-клавиатуры и сообщения. Два синглтона (`TelegramBot`, `PrismaClient`) шарятся через path-алиасы `@bot` и `@prisma`.
Одиночный Node.js процесс без HTTP-сервера. Весь UI — Telegram reply-клавиатуры и inline-кнопки.
Playwright запускается headless для каждой операции с hh.ru и закрывается после — никаких persistent browser-процессов. Для имитации человеческого поведения используются случайные задержки и скроллы.
```
src/
├── index.ts # точка входа, регистрация хендлеров
├── bot-singleton.ts # singleton TelegramBot (@bot)
├── prisma.ts # singleton PrismaClient (@prisma)
├── openai.ts # Groq API через openai-sdk
└── 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 # логин, OTP-флоу
├── info.ts # статус, проблемные вакансии
├── resume.ts # список резюме, просмотр, выбор
└── settings.ts # запрос, лимит, промпт, авто-режим
```
**Path-алиасы** (tsconfig.json): `@bot`, `@prisma`, `@/*``src/*`
Состояние диалога (`awaitingEmail`, `awaitingQuery` и т.д.) хранится in-memory в `Map<chatId, UserState>` — сбрасывается при рестарте процесса. Персистентные данные (сессия, резюме, настройки) — только в БД.
---
## База данных
```prisma
model User {
telegramId BigInt @unique
hhEmail 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 — legacy, в текущей версии не используется
# 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) |
**Информация**
| Кнопка | Действие |
|---|---|
| `⚙️ Статус` | Текущие настройки и статус авторизации |
| `📋 Моё резюме` | Просмотр сохранённого резюме |
| `🚫 Проблемные вакансии` | Вакансии с анкетой (бот не может откликнуться) |