Skip to content

Repository files navigation

Link Tracker

Link Tracker — многомодульный Java/Spring Boot проект для отслеживания обновлений по ссылкам и доставки уведомлений пользователям Telegram. Система хранит подписки пользователей, периодически проверяет внешние источники, формирует события обновлений и отправляет их через HTTP или Kafka-пайплайн с дополнительной обработкой в ai-agent.

Проект был реализован в рамках прохождения 2 семестра в Т-Академии, интеграционное тестирование через Testcontainers и запуск инфраструктуры через Docker Compose.

Содержание

Возможности

  • Регистрация Telegram-чата и управление подписками на ссылки.
  • Отслеживание GitHub, Stack Overflow и Stack Exchange ссылок.
  • Диалоговый сценарий добавления ссылки с тегами.
  • Просмотр всех подписок или подписок по тегу.
  • Удаление подписки по ссылке.
  • Периодическая проверка обновлений во внешних API.
  • Два режима доступа к БД Scrapper: SQL и ORM.
  • Liquibase-миграции PostgreSQL.
  • Kafka-доставка обновлений через outbox и DLQ.
  • HTTP-доставка обновлений в Bot с Kafka fallback.
  • AI Agent для фильтрации, группировки, приоритизации и сжатия сообщений.
  • Valkey-кэш списка ссылок в Scrapper.
  • Rate limiting и Resilience4J для HTTP-клиентов.
  • Метрики Micrometer/Prometheus и готовый Docker Compose для Grafana.
  • Интеграционные тесты с Testcontainers.

Архитектура

flowchart LR
    user["Telegram user"] --> telegram["Telegram Bot API"]
    telegram --> bot["bot :8080"]
    bot --> scrapper["scrapper :8081"]
    scrapper --> postgres[("PostgreSQL")]
    scrapper --> github["GitHub API"]
    scrapper --> stackoverflow["Stack Exchange API"]
    scrapper --> valkey[("Valkey")]
    scrapper --> rawTopic["Kafka: link.raw-updates"]
    rawTopic --> ai["ai-agent :8082"]
    ai --> processedTopic["Kafka: link.processed-updates"]
    processedTopic --> bot
    scrapper -. "HTTP transport" .-> bot
    bot --> prometheus["Prometheus /metrics"]
    scrapper --> prometheus
    prometheus --> grafana["Grafana"]
Loading

Основной поток в Kafka-режиме:

  1. Пользователь добавляет ссылку через Telegram-бота.
  2. Bot вызывает Scrapper API и регистрирует подписку.
  3. Scrapper по расписанию проверяет внешние источники.
  4. Новые события сохраняются в outbox и публикуются в link.raw-updates.
  5. AI Agent читает raw-события, фильтрует, группирует, приоритизирует и публикует результат в link.processed-updates.
  6. Bot читает processed-события из Kafka и отправляет сообщения пользователям.

В HTTP-режиме Scrapper отправляет обновления напрямую в Bot через POST /updates. При включенном fallback неуспешная HTTP-доставка может быть сохранена в Kafka outbox.

Структура проекта

.
├── ai-agent/                 # Kafka consumer/producer для интеллектуальной обработки обновлений
├── bot/                      # Telegram-бот и API приема готовых уведомлений
├── build-report-aggregate/   # Служебный модуль для агрегированных отчетов сборки
├── migrations/               # Liquibase changelog и SQL-миграции PostgreSQL
├── observability/            # Prometheus и Grafana provisioning
├── scrapper/                 # Сервис подписок, планировщик проверок и интеграции с внешними API
├── docker-compose.yaml       # Локальная инфраструктура и сервисы Bot/Scrapper
├── pom.xml                   # Parent POM
├── mvnw, mvnw.cmd            # Maven Wrapper
├── pmd.xml                   # PMD ruleset
└── spotbugs-excludes.xml     # Исключения SpotBugs

Модули

Модуль Назначение
bot Telegram-интерфейс, команды пользователя, REST endpoint /updates, Kafka consumer готовых уведомлений.
scrapper REST API подписок, PostgreSQL, Liquibase, scheduler, GitHub/Stack Overflow clients, Kafka/HTTP sender, Valkey cache.
ai-agent Читает сырые Kafka-обновления, применяет фильтры, summary, приоритеты и группировку, пишет обработанные обновления.
build-report-aggregate Служебный модуль для интеграционных проверок и отчетов по сборке.

Требования

  • JDK 25 или новее.
  • Maven 3.9.12. Рекомендуется использовать Maven Wrapper из репозитория.
  • Docker Desktop / Docker Engine с Docker Compose v2.
  • Telegram Bot token для полноценного запуска bot.
  • Опционально: GitHub token и Stack Exchange credentials для повышения лимитов внешних API.
  • IntelliJ IDEA для удобного запуска Spring Boot модулей локально.

Проверить Java и Docker:

java -version
docker version
docker compose version

Конфигурация

Секреты и локальные настройки задаются через переменные окружения. В репозитории есть безопасный шаблон .env.example.

Создать локальный .env:

Copy-Item .env.example .env

Для Unix-like окружения:

cp .env.example .env

После копирования заполните реальные значения. Файл .env добавлен в .gitignore; не коммитьте токены и пароли.

Основные переменные окружения

Переменная Используется Описание
TELEGRAM_TOKEN bot Токен Telegram-бота. Обязателен для реального Telegram-запуска.
SCRAPPER_BASE_URL bot URL Scrapper API. Для IDE обычно http://localhost:8081, внутри compose http://scrapper:8081.
BOT_BASE_URL scrapper URL Bot API для HTTP transport. Для IDE http://localhost:8080, внутри compose http://bot:8080.
POSTGRES_URL scrapper JDBC URL PostgreSQL.
POSTGRES_USER / POSTGRES_PASSWORD scrapper Учетные данные PostgreSQL.
ACCESS_TYPE scrapper Режим доступа к БД: SQL или ORM.
KAFKA_BOOTSTRAP_SERVERS все Kafka-модули Kafka bootstrap servers. Для IDE localhost:19092,localhost:29092,localhost:39092.
NOTIFICATION_TRANSPORT scrapper Основной транспорт уведомлений: KAFKA или HTTP.
NOTIFICATION_FALLBACK_ENABLED scrapper Включает Kafka fallback/outbox для HTTP-режима.
LINK_RAW_UPDATES_TOPIC scrapper, ai-agent Topic сырых обновлений. По умолчанию link.raw-updates.
LINK_RAW_UPDATES_DLQ_TOPIC scrapper DLQ для сырых обновлений. По умолчанию link.raw-updates-dlq.
LINK_PROCESSED_UPDATES_TOPIC ai-agent, bot Topic обработанных обновлений. По умолчанию link.processed-updates.
LINK_PROCESSED_UPDATES_DLQ_TOPIC bot DLQ для обработанных обновлений. По умолчанию link.processed-updates-dlq.
VALKEY_ENABLED scrapper Включает Valkey-кэш списка ссылок.
VALKEY_PASSWORD scrapper Пароль Valkey.
GITHUB_TOKEN scrapper GitHub token. Можно оставить dummy для локального smoke-run.
STACKOVERFLOW_KEY / STACKOVERFLOW_ACCESS_KEY scrapper Stack Exchange API credentials.
RATE_LIMIT_ENABLED bot, scrapper Включает rate limiting публичных endpoints.

AI Agent

Переменная Описание
AI_AGENT_CONSUMER_GROUP Consumer group для чтения link.raw-updates.
AI_AGENT_STOP_WORDS Stop words для фильтрации обновлений.
AI_AGENT_EXCLUDED_AUTHORS Авторы, обновления которых игнорируются.
AI_AGENT_MIN_LENGTH Минимальная длина сообщения после фильтрации.
AI_AGENT_SUMMARIZATION_THRESHOLD Порог длины текста для summary.
AI_AGENT_HIGH_KEYWORDS Ключевые слова высокого приоритета.
AI_AGENT_LOW_KEYWORDS Ключевые слова низкого приоритета.
AI_AGENT_GROUPING_WINDOW_MS Окно группировки обновлений.
AI_AGENT_GROUPING_FLUSH_INTERVAL_MS Интервал flush группировки.

Локальный запуск

1. Собрать проект

Windows:

.\mvnw.cmd clean package -DskipTests

Linux/macOS/WSL:

./mvnw clean package -DskipTests

2. Поднять инфраструктуру

Для запуска Spring Boot приложений из IDE поднимите только зависимости:

docker compose up -d postgres zookeeper kafka-1 kafka-2 kafka-3 kafka-init valkey-node-0 valkey-node-1 valkey-node-2 valkey-init

3. Запустить приложения из IntelliJ IDEA

Создайте Spring Boot run configuration для классов:

Модуль Main class Порт
scrapper backend.academy.linktracker.scrapper.ScrapperApplication 8081
bot backend.academy.linktracker.bot.BotApplication 8080, management 8011
ai-agent backend.academy.linktracker.ai.AiAgentApplication 8082

В каждую конфигурацию подключите переменные из корневого .env. Для запуска из IDE используйте localhost-значения из .env.example.

Рекомендуемый порядок запуска:

  1. scrapper
  2. ai-agent, если используется NOTIFICATION_TRANSPORT=KAFKA
  3. bot

Если вы хотите проверить прямую доставку без AI Agent, установите:

NOTIFICATION_TRANSPORT=HTTP

Тогда Scrapper будет отправлять обновления напрямую в Bot через HTTP.

Docker Compose

docker-compose.yaml поднимает:

  • PostgreSQL localhost:5432
  • Zookeeper localhost:2181
  • Kafka cluster из трех broker'ов localhost:19092, localhost:29092, localhost:39092
  • Kafka topics init container
  • Valkey cluster localhost:6379, localhost:6380, localhost:6381
  • Scrapper localhost:8081
  • Bot localhost:8080, management localhost:8011
  • Prometheus localhost:9090
  • Grafana localhost:3000

Перед запуском compose-сервисов bot и scrapper должны существовать executable jar'ы:

.\mvnw.cmd package -DskipTests

Запуск всего compose-файла:

docker compose --env-file .env up -d --build

Логи:

docker compose logs -f scrapper bot

Остановка:

docker compose down

Остановка с удалением volume'ов:

docker compose down -v

В текущем compose-файле нет отдельного контейнера ai-agent. Для полного Kafka-пайплайна запустите ai-agent из IDE/Maven рядом с compose-инфраструктурой или используйте NOTIFICATION_TRANSPORT=HTTP для прямой доставки Scrapper -> Bot.

Telegram-команды

Команда Назначение
/start Зарегистрировать текущий Telegram-чат в Scrapper.
/help Показать список доступных команд.
/track Начать диалог добавления ссылки. Бот запросит URL и теги.
/list Показать все отслеживаемые ссылки.
/list <tag> Показать ссылки по тегу.
/untrack <url> Прекратить отслеживание ссылки.
/cancel Отменить текущий диалог /track.

Поддерживаются ссылки с http и https схемами на домены GitHub, Stack Overflow и Stack Exchange.

HTTP API

Scrapper

Base URL при локальном запуске: http://localhost:8081.

Метод Endpoint Назначение
POST /tg-chat/{id} Зарегистрировать чат.
DELETE /tg-chat/{id} Удалить чат и его подписки.
GET /links Получить ссылки чата. Требуется header Tg-Chat-Id. Опционально ?tag=....
POST /links Добавить ссылку. Требуется header Tg-Chat-Id.
DELETE /links Удалить ссылку. Требуется header Tg-Chat-Id.

Пример добавления ссылки:

curl -X POST http://localhost:8081/links \
  -H "Content-Type: application/json" \
  -H "Tg-Chat-Id: 123" \
  -d '{"link":"https://github.com/spring-projects/spring-boot","tags":["spring"],"filters":[]}'

Bot

Base URL при локальном запуске: http://localhost:8080.

Метод Endpoint Назначение
POST /updates Принять готовое уведомление от Scrapper или другого producer'а.

OpenAPI UI при наличии запущенного сервиса обычно доступен по /swagger-ui/index.html, API docs — по /v3/api-docs.

Kafka

Используемые topics:

Topic Producer Consumer Назначение
link.raw-updates scrapper ai-agent Сырые события обновлений.
link.raw-updates-dlq scrapper операционная диагностика DLQ сырых событий.
link.processed-updates ai-agent bot Обработанные уведомления для отправки пользователю.
link.processed-updates-dlq bot операционная диагностика DLQ обработанных уведомлений.

Scrapper использует outbox: событие сначала фиксируется в PostgreSQL, затем плановый publisher отправляет его в Kafka и помечает как доставленное.

Хранилища и миграции

PostgreSQL

PostgreSQL хранит:

  • Telegram-чаты;
  • отслеживаемые ссылки;
  • связи чат-ссылка;
  • теги и фильтры;
  • outbox-сообщения.

Схема управляется Liquibase. Changelog находится в migrations/master.xml, SQL-файлы — в migrations/*.sql.

Valkey

Scrapper может использовать Valkey для кэширования списка ссылок. Основные настройки:

VALKEY_ENABLED=true
VALKEY_CACHE_TTL=10m
VALKEY_CLIENT_SIDE_CACHE_ENABLED=false

В compose поднимается Valkey cluster из трех узлов. Для Docker Compose сервис scrapper сейчас запускается с VALKEY_ENABLED=false, чтобы не зависеть от cluster-клиента внутри контейнера. Для IDE-запуска можно включить кэш через .env.

Наблюдаемость

Actuator endpoints имеют base path /. Prometheus endpoint замаплен на /metrics.

Сервис Метрики Health
scrapper http://localhost:8081/metrics http://localhost:8081/health
bot http://localhost:8011/metrics http://localhost:8011/health

Prometheus:

http://localhost:9090

Grafana:

http://localhost:3000

Локальные учетные данные Grafana из compose:

login: admin
password: admin

Тесты и проверки качества

Полная проверка проекта:

.\mvnw.cmd clean verify

Запуск тестов:

.\mvnw.cmd test

Запуск тестов конкретного модуля:

.\mvnw.cmd -pl scrapper test
.\mvnw.cmd -pl bot test
.\mvnw.cmd -pl ai-agent test

Запуск одного теста:

.\mvnw.cmd -pl ai-agent -Dtest=RawLinkUpdateKafkaConsumerIntegrationTest test

Статические проверки:

.\mvnw.cmd compile -am spotless:check modernizer:modernizer spotbugs:check pmd:check pmd:cpd-check

Автоформатирование:

.\mvnw.cmd spotless:apply

Сборка executable jar'ов без тестов:

.\mvnw.cmd package -DskipTests

Интеграционные тесты используют Testcontainers. Для них должен быть запущен Docker Engine.

Типовые проблемы

JDK version must be at least 25

Проект собирается на Java 25. Установите JDK 25 и проверьте, что IntelliJ IDEA и терминал используют именно его.

Maven version should, at least, be 3.9.12

Используйте Maven Wrapper: mvnw.cmd на Windows или ./mvnw на Unix-like системах.

Docker Compose не находит jar в target

Dockerfile'ы копируют jar из bot/target и scrapper/target. Перед docker compose up --build выполните:

.\mvnw.cmd package -DskipTests

Нет уведомлений в Kafka-режиме

Проверьте, что запущен ai-agent. Scrapper публикует сырые события в link.raw-updates, Bot читает уже обработанные события из link.processed-updates.

Для проверки без AI Agent переключите транспорт:

NOTIFICATION_TRANSPORT=HTTP

Полезные ссылки

About

Учебный pet-проект из Т-Академии: Телеграм-бот для отслеживания ссылок из GitHub и StackOverflow

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages