Асинхронная масштабируемая система для круглосуточного мониторинга бирж фриланса (Kwork.ru и Profi.ru) с интеллектуальной фильтрацией, локальной дедупликацией в SQLite, мгновенными интерактивными уведомлениями в Telegram и встроенным ИИ-генератором персонализированных откликов на базе OpenAI/OpenRouter/DeepSeek/Ollama.
- Ключевые возможности
- Архитектура системы
- Спецификация модулей
- Спецификация базы данных (SQLite)
- Спецификация конфигурации
- Интерфейс Telegram-бота
- ИИ-генерация откликов и стратегии ценообразования
- Установка и быстрый старт
- Развёртывание на VPS (Docker / Systemd)
- Безопасность и публикация
- Мультиплатформенный парсинг в реальном времени:
- Kwork.ru: сбор заказов напрямую из SSR-состояния биржи без запуска ресурсоемких браузеров (Chromium/Selenium), минимальное потребление RAM (<50 МБ).
- Profi.ru: мониторинг бэкофиса специалиста через GraphQL API с сессионной авторизацией, отсечением кросс-сейл каруселей и изоляцией новых заказов.
- Интеллектуальная фильтрация:
- Категории и рубрики (например, 11 — «Разработка и IT»).
- Позитивный поиск по ключевым словам (regex/подстроки).
- Негативный фильтр по стоп-словам (отсеивание «1С», «дипломы», «курсовые» и т.п.).
- Ограничение по минимальной и максимальной цене.
- Лимит на число поданных конкурентных заявок (чтобы реагировать первыми).
- Интерактивный Telegram-клиент:
- Мгновенные карточки заказов с HTML-форматированием, бюджетом, статистикой найма заказчика и полным описанием.
- Автоматическая загрузка и чтение вложений заказчика (
.pdf,.docx,.txt) с отправкой выжимки в чат. - Постоянная Reply-клавиатура на 9 кнопок под строкой ввода и Inline-кнопки под каждым заказом.
- Автоматическое разбиение длинных текстов (>4096 символов) на цепочки сообщений.
- 🤖 Генерация персональных откликов с помощью LLM:
- 3 стратегии формирования предложения: Оптимальный, Демпинг (на отзывы), По бюджету (Эксперт).
- RAG-инъекция подтвержденного портфолио (
config/portfolio.md): выбор строго релевантных кейсов без «галлюцинаций». - Копирование готового текста для вставки на биржу в один тап (блок
<code>).
- Надежность и автономность:
- Локальная база SQLite для дедупликации и ведения истории.
- Автоматическая ротация логов (до 5 файлов по 5 МБ) и автоочистка старых заказов (старше 30 дней).
- Защита от блокировок (рандомизированный Jitter, эмуляция браузерных заголовков, поддержка HTTP/SOCKS5 прокси).
flowchart TD
subgraph DataSources [Источники данных]
KW[Kwork.ru SSR Endpoint]
PR[Profi.ru GraphQL API]
end
subgraph CoreEngine [Асинхронное ядро сервиса]
KP[KworkParser: HTTPX Async Client]
PP[ProfiParser: GraphQL BoSearch Client]
PF[ProjectFilter: Правила отбора]
PS[ProjectStorage: SQLite DB]
end
subgraph Intelligence [ИИ-подсистема]
AP[Attachment Parser: PDF/DOCX/TXT]
AI[AIAssistant: AsyncOpenAI / Local LLM]
PM[Промпты и портфолио: config/prompts]
end
subgraph Delivery [Шина доставки и интерфейс]
TG[TelegramNotifier: Polling & Bot API]
USER((Пользователь в Telegram))
end
KW -->|SSR HTML| KP
PR -->|GraphQL JSON| PP
KP -->|KworkProject| PF
PP -->|KworkProject| PF
PF -->|Подходит под критерии| PS
PS -->|Новый уникальный заказ| TG
TG -->|Уведомление с кнопками| USER
USER -->|Генерация отклика| TG
TG -->|Запрос генерации| AI
KP -.->|Скачивание файлов| AP
AP -->|Текст ТЗ| AI
PM -->|Контекст портфолио и правил| AI
AI -->|Готовый JSON отклика| TG
TG -->|Отформатированный отклик| USER
| Модуль | Расположение | Назначение и реализуемая логика |
|---|---|---|
| Главный координатор | src/main.py |
Запуск сервиса, координация фоновых задач опроса, планировщик интервалов с Jitter, перехват сигналов завершения (SIGINT, SIGTERM), настройка логирования. |
| Парсер Kwork | src/parser.py |
Асинхронный HTTP-клиент (HTTPX), извлечение структурированного SSR-стейта из тегов <script> страницы kwork.ru/projects, скачивание и парсинг вложений (pypdf, python-docx). |
| Монитор Profi.ru | src/profi_parser.py |
Клиент GraphQL BoSearch, авторизация через cookies, парсинг цен, исключение кросс-сейл карусели (BoSearchCarousel), фильтрация только свежих заявок (only_fresh). |
| Авторизатор Profi | src/login_profi.py |
Вспомогательный скрипт Playwright для комфортного логина специалиста через Chrome и автоматического сохранения cookies в .env. |
| Фильтрация | src/filters.py |
Проверка проектов по белому списку категорий, ключевым словам (позитивный фильтр), стоп-словам (негативный фильтр), бюджету и числу конкурентных откликов. |
| Хранилище | src/storage.py |
Слой работы с SQLite: дедупликация, сохранение карточек и распарсенных вложений, реестр подписчиков бота, автоочистка записей старше 30 дней. |
| Telegram-интерфейс | src/telegram_bot.py |
Long-polling обработчик команд, отправка HTML-карточек с inline-кнопками, генератор Reply-клавиатуры, разбивка сообщений >4096 символов. |
| ИИ-ассистент | src/ai_assistant.py |
Интеграция с OpenAI-совместимыми эндпоинтами, загрузка контекста портфолио, динамический выбор режима ценообразования, валидация JSON-схемы ответа. |
| Модели данных | src/models.py |
Строго типизированные Pydantic-модели (KworkProject, AppConfig, FilterConfig, AIConfig, ProfiConfig). |
| Конфигурация | src/config.py |
Загрузка и валидация настроек из config/config.yaml с переопределением из переменных окружения (.env). |
Файл базы данных по умолчанию: data/kwork_parser.db.
| Колонка | Тип | Описание |
|---|---|---|
project_id |
TEXT |
Уникальный ID проекта на бирже (PK вместе с source). |
source |
TEXT |
Источник проекта: 'kwork' или 'profi' (PK). |
title |
TEXT |
Заголовок заказа. |
price_limit |
REAL |
Нижняя граница цены / фиксированная цена (в рублях). |
possible_price_limit |
REAL |
Верхняя граница вилки бюджета. |
offers_count |
INTEGER |
Количество откликов на момент парсинга. |
category_id |
INTEGER |
Числовой ID категории. |
category_name |
TEXT |
Текстовое название категории или специальности. |
url |
TEXT |
Прямая ссылка на страницу заказа. |
files_json |
TEXT |
JSON-сериализованный список вложенных файлов. |
attachments_text |
TEXT |
Извлеченный текстовый контент вложений (ТЗ, ТЗ из PDF/Word). |
client_name |
TEXT |
Имя или логин заказчика. |
client_hired_percent |
TEXT |
Процент нанятых фрилансеров у заказчика (для Kwork). |
notified |
INTEGER |
Флаг отправки уведомления в Telegram (0/1). |
created_at |
TIMESTAMP |
Дата и время добавления в БД (UTC). |
Индексы: idx_seen_projects_created_at по полю created_at для быстрой выборки недавних проектов и мгновенной очистки устаревших данных.
| Колонка | Тип | Описание |
|---|---|---|
chat_id |
INTEGER |
Уникальный числовой ID чата/пользователя в Telegram (PRIMARY KEY). |
is_active |
INTEGER |
Флаг подписки (1 — активен, 0 — приостановлен). |
created_at |
TIMESTAMP |
Дата первого взаимодействия (/start). |
Сервис поддерживает двухслойную конфигурацию: файл config/config.yaml (структурированный базовый конфиг) и переменные окружения .env (приоритет над YAML).
| Переменная | Тип | По умолчанию | Описание |
|---|---|---|---|
TELEGRAM_BOT_TOKEN |
String | "" |
Токен бота от @BotFather. |
TELEGRAM_CHAT_IDS |
String | "" |
Список chat ID через запятую (например, 12345678,98765432). |
POLL_INTERVAL_SECONDS |
Int | 60 |
Базовый интервал опроса биржи (в секундах). |
POLL_JITTER_SECONDS |
Int | 15 |
Случайный разброс времени задержки (+/- секунд). |
LOG_LEVEL |
String | INFO |
Уровень детализации логов (DEBUG, INFO, WARNING, ERROR). |
LOG_FILE |
String | logs/kwork_parser.log |
Путь к файлу логов (ротация 5 файлов по 5 МБ). |
DATABASE_PATH |
String | data/kwork_parser.db |
Путь к файлу SQLite. |
PROXY |
String | null |
Прокси-сервер (http://user:pass@host:port или socks5://...). |
AI_ENABLED |
Bool | true |
Включение генерации откликов через нейросеть. |
AI_API_KEY |
String | "" |
API-ключ (OpenAI, OpenRouter, DeepSeek и др.). |
AI_BASE_URL |
String | null |
Базовый URL API (например, https://openrouter.ai/api/v1). |
AI_MODEL |
String | gpt-4o-mini |
Название используемой модели. |
AI_CONTEXT_FILE |
String | config/portfolio.md |
Путь к файлу с подтвержденными кейсами разработчика. |
PROFI_ENABLED |
Bool | false |
Включение параллельного мониторинга Profi.ru. |
PROFI_COOKIES |
String | "" |
Сессионные cookies специалиста Profi.ru (p_uid=...; session_id=...). |
Под полем ввода сообщения всегда доступна клавиатура быстрого доступа:
| Кнопка | Действие |
|---|---|
📋 Заказы Kwork |
Показывает последние 10 проектов с Kwork с кнопками отклика. |
📋 Заказы Profi.ru |
Показывает последние 10 заказов с биржи Profi.ru. |
🔎 Проверить Kwork |
Принудительно запускает внеочередной парсинг Kwork. |
🔎 Проверить Profi.ru |
Принудительно запускает внеочередной парсинг Profi.ru. |
📊 Статус |
Аптайм сервиса, количество отслеженных заказов, статус ИИ. |
🎯 Фильтры |
Отображает действующие критерии (категории, слова, стоп-слова, цены). |
💼 Портфолио |
Показывает текущий файл портфолио разработчика. |
ℹ️ Справка |
Выводит полный список команд и подсказок. |
🛑 Пауза/Стоп |
Приостанавливает отправку новых уведомлений в текущий чат. |
Под каждой карточкой нового заказа крепятся интерактивные кнопки:
[🔗 Открыть проект на бирже]— прямая ссылка на страницу заказа.[⚡ Отклик (Оптимальный)]— генерация взвешенного коммерческого отклика.[📉 Демпинг (на отзывы)]— расчет сниженной ставки со скидкой ради гарантированного заказа и отзыва.[💼 По бюджету (Эксперт)]— максимальная планка бюджета с акцентом на архитектуру и тесты.
Готовый текст отклика отправляется в чат внутри блока <code>: для копирования достаточно одного клика по тексту на телефоне или компьютере.
Генерация отклика опирается на строгую структуру системных промптов в config/prompts/:
mode_optimal.md(Оптимальный режим):- Анализирует вилку цен заказчика, выставляет реалистичную конкурентную ставку.
- Фокусируется на пользе, архитектурном подходе и ключевых технических деталях из ТЗ.
mode_dumping.md(Демпинг ради отзывов):- Рассчитывает цену со скидкой (по умолчанию
35%от базовой планки, параметрdumping_discount_percent). - Формулирует предложение от первого лица без лишней воды, фиксирует точный срок и стоимость под ключ.
- Рассчитывает цену со скидкой (по умолчанию
mode_expert.md(Экспертный режим):- Верхняя планка бюджета заказчика.
- Включает полное сопровождение, написание автотестов, CI/CD и документацию.
- Контекст портфолио (
config/portfolio.md):- ИИ запрещено выдумывать неподтвержденный опыт. Он обязан выбрать из файла портфолио 1–2 наиболее похожих проекта разработчика и упомянуть конкретные детали реализации.
git clone https://github.com/XFN52/kwork-parser.git
cd kwork-parser# Создание venv
python -m venv venv
# Активация:
# Linux / macOS:
source venv/bin/activate
# Windows PowerShell:
.\venv\Scripts\Activate.ps1
# Установка зависимостей
pip install -r requirements.txtcp .env.example .env
cp config/config.example.yaml config/config.yamlОткройте .env и укажите:
TELEGRAM_BOT_TOKEN— токен вашего Telegram-бота.TELEGRAM_CHAT_IDS— ваш личный chat ID (можно узнать у @userinfobot).AI_API_KEY— ключ API (OpenAI / OpenRouter / DeepSeek).
Заполните config/portfolio.md своими реальными кейсами (шаблон с примерами находится в config/portfolio.example.md).
python -m src.mainСервис полностью упакован в легковесный Docker-контейнер на базе python:3.12-slim с запуском от непривилегированного пользователя appuser.
-
Установите Docker на сервер:
curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh apt install -y docker-compose-plugin -
Скопируйте проект на сервер и перейдите в каталог:
cd /opt/kwork-parser cp .env.example .env nano .env -
Запустите в фоновом режиме:
docker compose up -d --build
-
Мониторинг логов:
docker compose logs -f
Данные базы сохраняются в ./data, а логи — в ./logs на хосте через Docker volumes.
Если вы планируете мониторить не только Kwork, но и Profi.ru:
- Запустите локально утилиту авторизации:
python -m src.login_profi
- В открывшемся браузере войдите в свой профиль специалиста Profi.ru (по SMS или паролю).
- Скрипт перехватит сессию и автоматически сохранит
PROFI_COOKIESв ваш.env.
- Файлы секретов (
.env,config/config.yaml), личные базы данных (*.db) и профили браузеров (data/profi_browser_profile/) внесены в.gitignoreи.dockerignore. - В публичный репозиторий публикуются исключительно файлы примеров (
.env.example,config/config.example.yaml,config/portfolio.example.md). - Перед публикацией в публичный Git-репозиторий всегда проверяйте статус:
git status