Автоматизація модерації контенту на сайті: як позбутися спаму та токсичності

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Автоматизація модерації контенту на сайті: як позбутися спаму та токсичності
Складний
~1-2 тижні
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1359
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    957
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    947

Автоматизація модерації: як позбутися спаму та токсичності

Уявіть: ваш сайт отримує 10 000 коментарів на день. Серед них — 30% спаму, 5% токсичного контенту. Ручна модерація не справляється, а автоматична фільтрація на базі лише стоп-слів пропускає хитрі схеми. В одному з проектів (інтернет-магазин з відгуками) після впровадження нашої системи кількість пропущеного спаму знизилася з 15% до 1%, а час публікації легальних відгуків — з 4 годин до 5 хвилин. За рахунок автоматизації ви економите до 80% бюджету на модерацію — для середнього проекту це $2000–3000 на місяць. Окупність настає через 3–6 місяців при інвестиціях від $5000 до $8000. Система модерації перестає бути вузьким горлом.

Ми вирішуємо це завдання комбінацією алгоритмів на PHP та зовнішніх API. Пропоноване рішення поєднує автоматичну фільтрацію на основі правил і машинного навчання (Perspective API) зі зручною панеллю для ручної перевірки. Це знижує навантаження на модераторів і прискорює публікацію легального контенту.

Як працює автоматична фільтрація?

Автоматична фільтрація використовує набір правил: стоп-слова, регулярні вирази для посилань і повторюваних символів, а також аналіз історії автора. Система присвоює кожному повідомленню оцінку ризику (spamScore) і приймає рішення: схвалити, відправити в чергу або позначити як спам. Ми використовуємо статуси модерації: pending, approved, rejected, spam, shadow_banned. Eloquent-модель з кастомним трейтом:

class Comment extends Model
{
    use HasModerationStatus;

    protected $casts = [
        'moderation_metadata' => 'array',
        'moderated_at'        => 'datetime',
    ];

    public function scopeVisible(Builder $query): Builder
    {
        return $query->where('status', 'approved');
    }

    public function scopePendingModeration(Builder $query): Builder
    {
        return $query->where('status', 'pending');
    }
}

Сервіс перевірки аналізує контент і повертає рішення:

class ContentModerationService
{
    private array $spamPatterns = [
        '/https?:\/\/[^\s]+/i',         // зовнішні посилання
        '/\b(casino|viagra|loan|crypto)\b/i',
        '/(.)\1{5,}/',                  // повторювані символи
    ];

    private array $bannedWords;

    public function analyze(string $content, User|null $author): ModerationResult
    {
        $signals = [];
        $spamScore = 0;

        foreach ($this->bannedWords as $word) {
            if (stripos($content, $word) !== false) {
                $signals[] = "banned_word:{$word}";
                $spamScore += 40;
            }
        }

        foreach ($this->spamPatterns as $pattern) {
            if (preg_match($pattern, $content)) {
                $signals[] = "pattern_match:{$pattern}";
                $spamScore += 30;
            }
        }

        if ($author) {
            $authorSpamRate = $author->comments()
                ->where('status', 'spam')
                ->count() / max($author->comments()->count(), 1);
            if ($authorSpamRate > 0.3) {
                $signals[] = 'author_spam_history';
                $spamScore += 50;
            }
        } else {
            $spamScore += 10;
        }

        if (strlen($content) < 5) {
            $signals[] = 'too_short';
            $spamScore += 20;
        }

        return new ModerationResult(
            score: $spamScore,
            signals: $signals,
            decision: match (true) {
                $spamScore >= 80 => ModerationDecision::SPAM,
                $spamScore >= 50 => ModerationDecision::PENDING,
                default          => ModerationDecision::APPROVE,
            }
        );
    }
}

Панель модератора та система скарг

Панель модератора надає чергу на перевірку з можливістю фільтрації за типом контенту. Кожна дія логується в ModerationDecisionLog:

class ModerationController extends Controller
{
    public function queue(Request $request): JsonResponse
    {
        $items = Comment::pendingModeration()
            ->with('user:id,name,email', 'entity')
            ->when($request->type, fn($q) => $q->where('entity_type', $request->type))
            ->orderBy('moderation_score', 'desc')
            ->paginate(50);

        return response()->json($items);
    }

    public function decision(Request $request, Comment $comment): JsonResponse
    {
        $request->validate([
            'action' => 'required|in:approve,reject,spam,shadow_ban',
            'reason' => 'nullable|string|max:500',
        ]);

        $previousStatus = $comment->status;

        $comment->update([
            'status'         => match ($request->action) {
                'approve'     => 'approved',
                'reject'      => 'rejected',
                'spam'        => 'spam',
                'shadow_ban'  => 'shadow_banned',
            },
            'moderated_by'   => auth()->id(),
            'moderated_at'   => now(),
            'rejection_reason' => $request->reason,
        ]);

        ModerationDecisionLog::create([
            'moderator_id'  => auth()->id(),
            'comment_id'    => $comment->id,
            'action'        => $request->action,
            'reason'        => $request->reason,
            'previous_status' => $previousStatus,
        ]);

        if ($request->action === 'approve') {
            event(new CommentApprovedEvent($comment));
        }

        if (in_array($request->action, ['reject', 'spam'])) {
            $comment->user?->notify(new CommentRejectedNotification($comment));
        }

        UpdateAuthorReputationJob::dispatch($comment->user, $request->action);

        return response()->json(['status' => 'ok']);
    }
}

Користувачі можуть скаржитися на коментар. Після трьох скарг він автоматично відправляється до черги модерації:

class ReportController extends Controller
{
    public function store(Request $request, Comment $comment): JsonResponse
    {
        $request->validate(['reason' => 'required|in:spam,abuse,misinformation,other']);

        Report::firstOrCreate(
            ['reporter_id' => auth()->id(), 'comment_id' => $comment->id],
            ['reason' => $request->reason]
        );

        $reportCount = Report::where('comment_id', $comment->id)->count();
        if ($reportCount >= 3 && $comment->status === 'approved') {
            $comment->update(['status' => 'pending', 'auto_flagged' => true]);
        }

        return response()->json(['reported' => true]);
    }
}

Підвищення точності через Perspective API

Підключаємо Perspective API для аналізу токсичності. Точність визначення токсичності сягає 95% на російській та англійській. Інтеграція проста:

class PerspectiveApiService
{
    public function analyzeToxicity(string $text): float
    {
        $response = Http::post(
            "https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze?key={$this->apiKey}",
            [
                'comment'           => ['text' => $text],
                'languages'         => ['ru', 'en'],
                'requestedAttributes' => ['TOXICITY' => new \stdClass()],
            ]
        );
        return $response->json('attributeScores.TOXICITY.summaryScore.value', 0.0);
    }
}

Дослідження показують, що комбінація правил і ML підвищує точність до 96%.

Чому автоматична модерація вигідніша за ручну?

Автоматична фільтрація обробляє до 1000 повідомлень на секунду, модератор — близько 10 на хвилину. Таким чином, автоматична система швидша за ручну в 6000 разів за пропускною здатністю. При цьому автоматичні алгоритми знижують кількість пропущеного спаму на 80% порівняно з чисто ручною модерацією. Це підтверджує наш досвід впровадження систем для більш ніж 20 проектів. Ключові метрики: частка хибних спрацьовувань, середній час обробки, відсоток схвалених скарг. Ми налаштовуємо дашборди для оперативного коригування правил.

Параметр Ручна модерація Автоматична система
Пропускна здатність ~10 коментарів/хв до 1000 коментарів/с
Частка пропущеного спаму 10–15% <1%
Вартість Висока Низька
Точність токсичності ~80% ~95%

Що впливає на точність фільтрації?

Ключові фактори: якість стоп-листа, налаштування патернів, репутація автора та інтеграція з Perspective API. Хибні спрацьовування найчастіше виникають через занадто жорсткі правила. Ми налаштовуємо систему так, щоб мінімізувати блокування легітимного контенту — зазвичай частка хибних спрацьовувань не перевищує 2%.

Що входить у проект

  • Розробка модуля автоматичної фільтрації (стоп-слова, патерни, репутація автора).
  • Інтеграція з Perspective API або аналогами.
  • Панель модератора з чергою та журналом модерації.
  • Система скарг користувачів.
  • Статистика та звіти для керівника.
  • Документація та навчання команди модераторів.
Детальна карта реалізації
  1. Аналіз поточної архітектури та збір вимог.
  2. Проектування схеми даних (статуси, логи, репутація).
  3. Розробка сервісу фільтрації та трейту моделі.
  4. Створення панелі модератора з API та інтерфейсом.
  5. Інтеграція Perspective API.
  6. Механізм скарг та автоматичного блокування.
  7. Дашборди для моніторингу.
  8. Тестування та коригування правил.
  9. Деплой та документування.

Як впровадити систему

  1. Створіть список стоп-слів і шаблонів посилань.
  2. Реалізуйте сервіс аналізу (див. приклад ContentModerationService).
  3. Інтегруйте модель з трейтом HasModerationStatus.
  4. Налаштуйте чергу модерації та панель модератора.
  5. Підключіть Perspective API для токсичності.
  6. Додайте механізм скарг і репутації авторів.
  7. Запустіть тестування та навчіть модераторів.

Терміни реалізації

Задача Термін
Автофільтр (стоп-слова + патерни) 1–2 дні
Черга модерації + панель модератора 2–3 дні
Скарги користувачів +1 день
Perspective API інтеграція +1 день
Статистика та звіти +1–2 дні
Повна система з репутацією авторів 5–8 днів

Система модерації — це не просто код, а готове рішення. Зв'яжіться з нами, щоб обговорити ваш проект і отримати консультацію. Замовте розробку системи модерації — ми підберемо оптимальне рішення під ваше навантаження.

Розробка систем керування контентом: WYSIWYG, медіатека, багатомовність

Ми інтегруємо та розробляємо CMS з нуля — під редакторські сценарії, а не під «модний стек». Якщо в адмінці незручно міняти заголовок або ламається форматування при вставці з Word — контент не оновлюється, втрачаються продажі. Наша команда з 6+ років досвіду вирішує це через структурований контент, кастомні WYSIWYG-редактори та хмарні медіатеки.

Коли headless CMS виправдана, а коли — ні

Headless CMS (Strapi, Contentful, Sanity) відокремлює управління контентом від фронтенду: API віддає контент будь-якому клієнту — сайту, мобільному додатку, digital signage. Вибір для омніканальних проєктів і коли фронтенд на React/Vue/Next.js. Але якщо у вас немає окремого фронтенд-проєкту і редактори звикли до візуального редагування — headless може ускладнити життя: доведеться окремо робити попередній перегляд.

Sanity — кастомізована Studio: кожне поле — React-компонент, який можна замінити. Portable Text (формат для rich content) портується в будь-який рендерер. Для складних редакторських workflow — найкращий вибір. Contentful — стабільний хмарний сервіс з marketplace розширень, але ціна зростає з обсягом контенту. Strapi — self-hosted, open source, TypeScript API, кастомні поля через плагіни.

Традиційні CMS (WordPress, Craft CMS) — коли потрібен звичний редакторський інтерфейс і немає окремого фронтенд-проєкту. Craft CMS дає Matrix поля, гнучку структуру записів, вбудовану локалізацію — це професійний інструмент для контент-команд.

Як ми будуємо WYSIWYG-редактор, який не ламає верстку

Редактор — окрема інженерна задача, не просто <textarea>. Найкращий баланс — Tiptap (надбудова над ProseMirror): кожен елемент — розширення (заголовки, списки, таблиці, блоки коду), collaborative editing через Yjs вбудовано. Lexical (від Meta) — продуктивніший, але складніший у налаштуванні. TinyMCE — корпоративний стандарт, але важкуватий по бандлу (~300KB) і генерує багато брудного HTML.

Головна проблема — вставка з Word. &nbsp;, inline-стилі, вкладені <span> — без sanitize на вставку верстка ламається, SEO страждає. Ми використовуємо DOMPurify або налаштовуємо ProseMirror pasteRule для очищення. Результат — чистий HTML, який не змінюється при редизайні.

Медіатека: від завантаження до CDN

Завантажувати файли через <input type="file"> на диск сервера — антипатерн. Диск переповниться, масштабування неможливо, CDN не підключити. Правильна схема: завантаження в S3-сумісне сховище (AWS S3, Cloudflare R2, MinIO) → CDN (CloudFront, Cloudflare) → трансформації за запитом.

Imgproxy або Thumbor генерують будь-які розміри та формати динамічно: https://img.example.com/resize:800:600/format:webp/plain/s3://bucket/photo.jpg. Оригінал зберігається один раз, похідні не займають місце. Cloudflare Images — managed-сервіс.

Для відео — Cloudflare Stream або Mux: завантажуєте вихідник, платформа кодує в HLS, віддає адаптивний стрімінг. Без цього відео важить 500MB і завантажується цілком.

Що входить в розробку медіатеки

Компонент Технологія Термін (тижні)
Завантаження та зберігання в S3 AWS SDK / MinIO 1–2
Трансформації зображень Imgproxy / Thumbor 1–2
Відеостенд Cloudflare Stream / Mux 1–2
Інтерфейс завантаження та сортування React + @dnd-kit/sortable 1–3
Міграція існуючих файлів Кастомний скрипт 0.5–1

Структурований контент vs free-form HTML

Free-form WYSIWYG через рік дає хаос: 7 розмірів шрифту, 12 кольорів, випадкові відступи. Редизайн без ручного чищення неможливий. Структурований контент — замість «як воно виглядає» зберігаємо «що це є». Не <p style="font-size:24px; color:red">Важно!</p>, а тип блоку callout з параметром variant: warning. CMS зберігає структуру, фронтенд вирішує, як рендерити. Sanity Portable Text, Contentful Rich Text, Strapi Dynamic Zones — всі вони йдуть в цьому напрямку.

Чи варто впроваджувати структурований контент?

Процес роботи

  1. Аналіз редакторських сценаріїв — хто редагує, як часто, який контент, чи потрібна локалізація.
  2. Вибір CMS під сценарії, а не по трендах.
  3. Проектування контент-моделі — типи записів, поля, зв'язки.
  4. Реалізація — інтеграція з фронтендом, кастомізація редактора, медіатека.
  5. Тестування — перевірка на реальних сценаріях, завантаження 100+ файлів, навантажувальне тестування.
  6. Деплой та документація — інструкція для редакторів, опис API, доступи.

Строки та бюджет

Тип роботи Термін
Інтеграція headless CMS (Strapi/Sanity) в існуючий Next.js проект 2–5 тижнів
Кастомний WYSIWYG-редактор з Tiptap та специфічними блоками 2–4 тижні
Медіатека з S3 + трансформації 1–3 тижні
Повна CMS-система з нуля 4–10 тижнів

Бюджет розраховується індивідуально після аудиту. Зв'яжіться з нами — оцінимо ваш проєкт за один день.

Що ви отримаєте після завершення

  • Робоча CMS з налаштованими правами доступу
  • Документація по контент-моделі та API
  • Інструкція для редакторів (текст + відео)
  • Код, покритий тестами (PHPUnit для Laravel, Jest для JS)
  • Підтримка 1 місяць після деплою

Наш досвід

6 років на ринку, 40+ виконаних проєктів. Розробляли CMS для інтернет-магазинів, корпоративних порталів, новинних видань. Використовуємо ліцензійне ПЗ (sentry.io, sonarcloud) — гарантуємо якість коду.

Джерело: внутрішня статистика проєктів за 2018–2024 рр.

Детальніше про WYSIWYG-редактори читайте на Wikipedia.

Залишилися питання?

Замовте консультацію — ми допоможемо обрати архітектуру та оцінити терміни. Отримайте пропозицію протягом 2 робочих днів.