Pavel SkvortsovPavel Skvortsov
← Back
03

WP Block - Hot Offers

A two-part WordPress feature: an infinite CSS marquee carousel of top-performing miners, and a Telegram webhook that automatically parses price lists from a channel and updates product prices in the catalog.

WordPressGutenbergReactTelegram APIPHP

"The manager posts a price list in Telegram. The catalog updates itself."

CSS
marquee - no JS animation, pure keyframes
webhook
Telegram - WordPress REST API - ACF update
2%
hashrate matching tolerance for price slot lookup
4
fallback states: updated - new hashrate - slots full - not found
How it works
Telegram channel post
Webhook - /wp-json/ms/v1/tg-webhook
Secret token validation
Parse price lines (regex)
Normalize model names
Match - update_field()
Report back to Telegram
Project Structure
parts/hot-deals.phpqueries miners, scores by daily income, selects top 4, triples array for CSS loop
inc/tg-channel.phpREST webhook handler, price parser, normalizer, slot matcher, report sender
POST /wp-json/ms/v1/tg-webhookreceives Telegram channel_post, validates secret, routes: price message vs regular post
GET /wp-json/ms/v1/tg-postsreturns last 5 channel posts from transient, falls back to scraping t.me/s/channel
ms2026_pending_prices optionstores unmatched price lines for manual review - new hashrate variants and unknown models
Notable detail

When a price line cannot be matched - new hashrate variant or product not yet in catalog - it is stored in a pending_prices option for manual review. The manager gets an itemized Telegram report: what was updated, what needs attention, and which products have not had prices updated in 14+ days.

Code
// Price message detection + line parser
  function ms2026_is_price_message(string $text): bool {
      return mb_strpos($text, '🔵') === 0
          && mb_strpos($text, 'в наличии РФ') !== false;
  }
  
  // Each line:  ModelName 100TH  1500 USDT
  preg_match(
      '/\s*(бу\s+)?(.+?)\s+([\d,\.]+)\s*(Mh|Th|TH|GH|kSol)\s*[-]\s*([\d\s,\.]+)\s*USDT/iu',
      $line, $m
  );
  
  // Normalize model name for fuzzy matching
  function ms2026_normalize_name(string $name): string {
      $name = mb_strtolower(trim($name));
      $name = preg_replace('/^бу\s+/u', '', $name);
      $name = str_replace('+', 'plus', $name);
      foreach (['hydro','pro','mini','home','plus'] as $suffix) {
          $name = preg_replace('/\s*'.$suffix.'\s*/u', ' '.$suffix.' ', $name);
      }
      return trim(preg_replace('/\s+/', ' ', $name));
  }
  
  // Match hashrate with 2% tolerance across H1H4 slots
  for ($i = 1; $i <= 4; $i++) {
      $slot_th = get_field('h'.$i.'_th', $pid);
      $diff = abs((float)$slot_th - $parsed['hashrate']);
      if ($diff <= $parsed['hashrate'] * 0.02) {
          update_field('h'.$i.'_price', $parsed['price'], $pid);
          break;
      }
  }
Features
  • Hot Deals carousel: selects top miners by daily income - up to 2 "Новинка" badges + top by ROI
  • Cards triplicated in PHP for seamless CSS loop - no JS cloning required
  • Animation speed calculated dynamically: card_count - (280px + 24px gap) - 60px/s
  • Pause on hover via CSS animation-play-state, respects prefers-reduced-motion
  • Telegram webhook receives channel_post events via REST API endpoint
  • Secret token validated with hash_equals() - constant-time comparison, timing-attack safe
  • Price message detection: starts with 🔵, contains "в наличии РФ"
  • Regex parser extracts model name, hashrate (TH/GH/kSol/MH normalized to TH), price in USDT
  • Model name normalization: lowercase, strip "бу", expand "+", add spaces before suffixes (pro/hydro/mini)
  • Hashrate slot matching with 2% tolerance - finds correct H1H4 slot per product
  • Unmatched prices stored in ms2026_pending_prices option for manual review
  • Report sent back to Telegram: updated / new hashrate variant / slots full / not found in catalog
  • Fallback: if webhook transient is empty, scrapes t.me/s/channel HTML for last 5 posts