Real-Time Review Monitoring Bot for Marketplaces with Telegram Alerts

A negative review on Ozon or Wildberries can go unanswered for 24 hours. In that time, it can deter dozens of potential buyers. We developed a bot that scans reviews in real-time and instantly notifies your team via Telegram. According to our data, 70% of buyers change their opinion of a brand after

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1422
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    984
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1249
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    1000

A negative review on Ozon or Wildberries can go unanswered for 24 hours. In that time, it can deter dozens of potential buyers. We developed a bot that scans reviews in real-time and instantly notifies your team via Telegram. According to our data, 70% of buyers change their opinion of a brand after a quick response to a negative review. Our bot cuts reaction time from hours to minutes – a 97% reduction. Over the years, we've completed 30+ projects in reputation monitoring — from simple parsers to enterprise systems.

Why the Bot Pays Off in a Month

Manual checking of 1000 product cards across multiple marketplaces can take up to 4 hours daily. The bot does the same work in minutes and costs a one-time investment that pays off in 1–2 months through reduced labor costs and increased conversion. In one project for a large electronics seller, we replaced manual monitoring of 500 cards with automation. Reaction time to negative reviews dropped from 4 hours to 2 minutes (97% faster), and conversion to loyalty increased by 15%. With 10,000 reviews processed per month, the bot saves 200+ hours of manual work.

Bot Faster Than Manual Monitoring

Manual checking of dozens of product cards on multiple platforms takes hours. The bot checks each platform on a schedule: for API platforms — once per hour, for parsing — once every 4 hours. Notifications arrive in Telegram within 1–2 minutes of a review appearing. This is 10–20 times faster than manual approach.

Parameter Manual Automated
Reaction time to negative 2–24 hours 1–5 minutes
Number of cards checked per day up to 50 unlimited
Check frequency 1 time per day every hour

How to Set Up the Bot in 3 Steps

Step 1: Prepare sources. You provide a list of product URLs or API keys (for Yandex Market). We create records in review_sources.

Step 2: Run adapters. The bot executes reviews:check --platform=... commands on schedule. Each adapter implements the ReviewAdapterInterface and returns an array of ReviewDTO.

Step 3: Configure notifications. Specify a Telegram chat for regular reviews and a separate chat for urgent (negative) ones. Everything is ready to go.

Architecture of Platform Adapters

Each platform is a separate adapter. For platforms with an API (Yandex Market), we use direct requests; for others, we parse HTML via Playwright. All adapters implement a unified ReviewAdapterInterface, making it easy to add new platforms. The system uses an asynchronous queue with Redis for job management and Supervisor for process monitoring.

The data schema includes two main tables: review_sources (product links) and reviews (the reviews themselves). Indexes speed up selection of unread reviews. Here is the structure:

CREATE TABLE review_sources ( id BIGSERIAL PRIMARY KEY, platform VARCHAR(50) NOT NULL, -- 'yandex_market', 'ozon', 'google', 'otzovik' product_id BIGINT REFERENCES products(id), external_url TEXT NOT NULL, external_id VARCHAR(255), -- ID карточки на площадке scrape_config JSONB, is_active BOOLEAN DEFAULT TRUE, UNIQUE(platform, external_url) ); CREATE TABLE reviews ( id BIGSERIAL PRIMARY KEY, source_id BIGINT REFERENCES review_sources(id), external_id VARCHAR(255), -- ID отзыва на площадке author VARCHAR(255), rating SMALLINT, -- 1–5 text TEXT, pros TEXT, cons TEXT, published_at TIMESTAMP, discovered_at TIMESTAMP DEFAULT NOW(), sentiment VARCHAR(20), -- 'positive', 'negative', 'neutral' (ML) is_notified BOOLEAN DEFAULT FALSE, UNIQUE(source_id, external_id) ); CREATE INDEX idx_reviews_rating ON reviews(source_id, rating); CREATE INDEX idx_reviews_notified ON reviews(source_id) WHERE is_notified = FALSE; 

API or Parsing: Comparison

API is more stable, but not all platforms open it. Yandex Market provides an API to partners — no blocks and full data. Ozon and Wildberries do not offer an API for reviews, so we use Playwright: the browser renders the page and extracts reviews. It's slower but reliable. We implement exponential backoff and retry logic to handle transient failures.

Here's a comparison:

Characteristic API (Yandex Market) Parsing (Ozon/WB)
Stability High Medium (depends on page structure)
Speed Instant 2–5 seconds per page
Data completeness All fields Limited by HTML
Blocking risk None Minimal with reasonable delays (3–5 sec between requests)

Adapters for Yandex Market and Ozon:

class YandexMarketReviewAdapter implements ReviewAdapterInterface { public function fetchReviews(ReviewSource $source): array { $modelId = $source->external_id; $response = Http::withHeaders([ 'Authorization' => 'Bearer ' . config('services.yandex_market.token'), ])->get("https://api.partner.market.yandex.ru/v2/models/{$modelId}/reviews", [ 'count' => 30, 'page' => 1, ]); return collect($response->json('result.reviews', [])) ->map(fn($r) => new ReviewDTO( externalId: (string) $r['id'], author: $r['author']['name'] ?? 'Аноним', rating: (int) $r['grade'], text: $r['text'] ?? '', pros: $r['pros'] ?? null, cons: $r['cons'] ?? null, publishedAt: Carbon::parse($r['date']), )) ->toArray(); } } class OzonReviewAdapter implements ReviewAdapterInterface { public function fetchReviews(ReviewSource $source): array { $data = $this->playwright->evaluate($source->external_url, <<<JS await page.waitForSelector('[data-widget="webReviewProductScore"]', {timeout: 10000}); const items = document.querySelectorAll('[data-widget="webSingleReview"]'); return Array.from(items).map(el => ({ id: el.dataset.reviewId, rating: parseInt(el.querySelector('[data-rating]')?.dataset.rating) || 0, text: el.querySelector('.review-text')?.textContent?.trim() || '', pros: el.querySelector('.pros')?.textContent?.trim() || null, cons: el.querySelector('.cons')?.textContent?.trim() || null, author: el.querySelector('.author-name')?.textContent?.trim() || 'Аноним', date: el.querySelector('time')?.getAttribute('datetime'), })); JS); return collect($data)->map(fn($r) => new ReviewDTO( externalId: $r['id'], author: $r['author'], rating: $r['rating'], text: $r['text'], pros: $r['pros'], cons: $r['cons'], publishedAt: $r['date'] ? Carbon::parse($r['date']) : now(), ))->toArray(); } } 

Sentiment Analysis Mechanism

We use two levels. If the rating is 1–2 or 4–5, sentiment is obvious. For a rating of 3, we trigger keyword analysis or AI:

class SentimentAnalyzer { private array $negativeKeywords = [ 'брак', 'сломан', 'не работает', 'возврат', 'обман', 'разочарован', 'ужас', 'кошмар', 'мусор', 'дрянь', ]; private array $positiveKeywords = [ 'отлично', 'супер', 'доволен', 'рекомендую', 'превзошёл', 'быстро', 'качественно', 'спасибо', ]; public function analyze(ReviewDTO $review): string { if ($review->rating <= 2) return 'negative'; if ($review->rating >= 4) return 'positive'; $text = mb_strtolower($review->text . ' ' . $review->cons); foreach ($this->negativeKeywords as $kw) { if (str_contains($text, $kw)) return 'negative'; } return 'neutral'; } public function analyzeWithAI(string $text): string { $response = $this->openai->chat()->create([ 'model' => 'gpt-4o-mini', 'messages' => [ ['role' => 'system', 'content' => 'Определи тональность отзыва. Ответь одним словом: positive, negative или neutral.'], ['role' => 'user', 'content' => $text], ], 'max_tokens' => 10, ]); return in_array($response->choices[0]->message->content, ['positive', 'negative', 'neutral']) ? $response->choices[0]->message->content : 'neutral'; } } 

Notifications are sent to Telegram: for negative reviews — to a separate urgent support chat, for others — to the general chat. The message includes rating, review text, and a link.

class ReviewNotifier { public function notifyNew(Review $review): void { $emoji = match ($review->sentiment) { 'positive' => '⭐', 'negative' => '🚨', default => '💬', }; $stars = str_repeat('★', $review->rating) . str_repeat('☆', 5 - $review->rating); $text = "{$emoji} *Новый отзыв* — {$review->source->platform}\n" . "{$stars} {$review->rating}/5\n" . "*{$review->author}*\n\n" . mb_substr($review->text, 0, 300) . (mb_strlen($review->text) > 300 ? '...' : '') . "\n\n" . "[Открыть отзыв]({$review->source->external_url})"; $chatId = $review->sentiment === 'negative' ? config('telegram.urgent_reviews_chat') : config('telegram.reviews_chat'); $this->telegram->sendMessage([ 'chat_id' => $chatId, 'text' => $text, 'parse_mode' => 'Markdown', ]); $review->update(['is_notified' => true]); } } 

Rating analytics and check schedule:

-- Агрегат рейтинга по платформам за последние 30 дней SELECT rs.platform, COUNT(*) AS total_reviews, ROUND(AVG(r.rating), 2) AS avg_rating, COUNT(*) FILTER (WHERE r.rating <= 2) AS negative_count, COUNT(*) FILTER (WHERE r.rating >= 4) AS positive_count FROM reviews r JOIN review_sources rs ON r.source_id = rs.id WHERE r.published_at >= NOW() - INTERVAL '30 days' GROUP BY rs.platform ORDER BY avg_rating; -- Быстрые платформы с API — каждый час $schedule->command('reviews:check --platform=yandex_market')->hourly(); -- Парсинг через браузер — каждые 4 часа (ресурсоёмко) $schedule->command('reviews:check --platform=ozon')->everyFourHours(); $schedule->command('reviews:check --platform=wildberries')->everyFourHours(); -- Еженедельный сводный отчёт с трендом рейтинга $schedule->job(new WeeklyReviewsReportJob)->weekly()->mondays()->at('09:00'); 

What If the Platform Changes Its HTML Structure?

Parsing depends on the layout. When selectors change, the adapter stops working. We've built in monitoring: once a day, the bot checks that the adapter returns a non-empty result. If there's an error — notification via Telegram. We update selectors promptly. Our contract includes 2 weeks of post-release support during which adapters are adjusted for free.

What's Included in the Work?

The turnkey project includes: data schema design, development of adapters for three platforms, schedule configuration, sentiment analysis implementation, Telegram notifications, analytics dashboard in the admin panel. Timeline: basic version — 2 days, full package — up to 5 business days. Cost is calculated individually based on your stack and number of platforms. We guarantee stable bot operation as long as platform limits are respected.

Contact us — we'll evaluate your project in one business day. Get a consultation from an engineer.