Про ИИ, который "ничего не умеет"

Про ИИ, который "ничего не умеет"

SiteBot·

Я считаю что ИИ без твоей экспертизы это ноль.

— /2025/10/07/ford-ceo-reveals-why-he-sent-his-son-to-work-as-a-welder/#comment-52591

Попробуем это опровергнуть?

В сети повсеместно встречаются мнения, что "ИИ тупой", "ничего не умеет", программистов он не заменит и, что самое главное - якобы никто не пострадает от этой "скатерти-самобранки".

Я не JavaScript программист, мои познания в этом языке остались на уровне 2015 года. О технологии WebRTC ничего не знаю. Нашёл анонимный видеочат без регистрации https://chatroulett.ru и поставил цель - добавить блокировку пользователей одной кнопкой. В качестве инструмента - https://chat.deepseek.com.

За пару часов мучений, связанных с перехватом WebRTC и корректным обнаружением IP-адресов кандидатов, мы пришли к работающему нетривиальному коду. И это не какой-то там CRUD, не багфиксы на несколько строк, а полноценная работающая программа со сложной логикой.

Профессия "программист" в данном виде существует последние годы.

Общее описание

Скрипт представляет собой довольно сложный инструмент для "управления" видеочатом chatroulett.ru. Он вмешивается в низкоуровневый процесс установления WebRTC-соединений (используя RTCPeerConnection, setRemoteDescription, addIceCandidate), анализирует сетевые параметры (IP-адреса из SDP и ICE-кандидатов) и блокирует соединения на основе пользовательского черного списка, хранящегося в localStorage. Он также предоставляет удобный интерфейс (кнопка "BAN") для добавления IP-адресов в черный список вручную и автоматически переходит к следующему собеседнику при обнаружении или добавлении заблокированного IP:

Программный код (Java Script):

// ==UserScript==
// @name         Chatroulett WebRTC Blocker
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Мгновенная блокировка нежелательных WebRTC соединений для chatroulett.ru
// @author       Your Name
// @match        *://chatroulett.ru/*
// @match        *://*.chatroulett.ru/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Инициализация черного списка
    const STORAGE_KEY = 'webrtc_blacklist';
    const RECENT_IPS_KEY = 'webrtc_recent_ips';

    // Функция для получения актуального черного списка
    const getBlacklist = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');

    // Разделенные списки исключений
    const EXCLUDED_DOMAINS = [
        'chatroulett.ru',
        'api.chatroulett.ru',
        'webrtc.chatroulett.ru'
    ];

    const EXCLUDED_IP_RANGES = [
        '0.0.0.0/8',              // Текущая сеть
        '10.0.0.0/8',             // Частная сеть
        '100.64.0.0/10',          // CGNAT
        '127.0.0.0/8',            // Loopback
        '169.254.0.0/16',         // Link-local
        '172.16.0.0/12',          // Частная сеть
        '192.0.0.0/24',           // Зарезервировано IANA
        '192.0.2.0/24',           // TEST-NET-1
        '192.168.0.0/16',         // Частная сеть
        '198.18.0.0/15',          // Тестовые сети
        '198.51.100.0/24',        // TEST-NET-2
        '203.0.113.0/24',         // TEST-NET-3
        '224.0.0.0/4',            // Multicast
        '240.0.0.0/4',            // Зарезервировано
        '255.255.255.255/32',     // Broadcast
        '7.0.0.0/8',              // Министерство обороны США
        '26.0.0.0/8',             // Министерство обороны США
        '28.0.0.0/8',             // Министерство обороны США
        '14.0.0.0/8',             // Китай (публичная сеть, но часто используется для NAT)
        // подсеть хостинга видеочата
        '93.189.228.0/22'
    ];

    // Функция для преобразования IP в число
    const ipToNumber = (ip) => {
        const parts = ip.split('.');
        if (parts.length !== 4) return 0;

        let result = 0;
        for (let i = 0; i < 4; i++) {
            const part = parseInt(parts[i], 10);
            if (isNaN(part) || part < 0 || part > 255) return 0;
            result += part * Math.pow(256, 3 - i);
        }
        return result;
    };

    // Функция проверки диапазона в CIDR нотации
    const isIPInRangeCIDR = (ip, cidr) => {
        const [network, prefix] = cidr.split('/');
        const prefixLength = parseInt(prefix, 10);

        const ipNum = ipToNumber(ip);
        const networkNum = ipToNumber(network);
        const mask = ~((1 << (32 - prefixLength)) - 1) >>> 0;

        return (ipNum & mask) === (networkNum & mask);
    };

    // Улучшенная функция проверки валидности IP
    const isValidIP = (ip) => {
        if (typeof ip !== 'string' || ip.trim() === '') {
            return false;
        }

        if (/[^0-9a-fA-F.:]/.test(ip)) {
            return false;
        }

        // IPv4
        const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
        if (ipv4Regex.test(ip)) {
            return true;
        }

        // IPv6 (упрощенная проверка)
        const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
        const ipv6ShortRegex = /^((?:[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4})*)?)::((?:[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4})*)?)$/;
        if (ipv6Regex.test(ip) || ipv6ShortRegex.test(ip)) {
            return true;
        }

        return false;
    };

    // Улучшенная функция проверки исключений
    const isExcluded = (value) => {
        // Проверка специальных адресов
        if (value === '0.0.0.0' ||
            value === '::' ||
            value === '0000:0000:0000:0000:0000:0000:0000:0000' ||
            value === '255.255.255.255' ||
            value.startsWith('192.0.2.') ||
            value.startsWith('198.51.100.') ||
            value.startsWith('203.0.113.') ||
            value.startsWith('169.254.')
        ) {
            return true;
        }

        // Проверка доменов
        if (!isValidIP(value)) {
            return EXCLUDED_DOMAINS.some(domain => value.includes(domain));
        }

        // Проверка CIDR диапазонов
        for (const cidr of EXCLUDED_IP_RANGES) {
            if (isIPInRangeCIDR(value, cidr)) {
                return true;
            }
        }

        return false;
    };

    // Функция для сохранения черного списка
    const saveBlacklist = (blacklist) => {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(blacklist));
    };

    // Функция для добавления IP в список последних
    const addToRecentIPs = (ip) => {
        if (!isValidIP(ip) || isExcluded(ip)) {
            return;
        }

        let recentIPs = JSON.parse(localStorage.getItem(RECENT_IPS_KEY) || '[]');
        recentIPs = recentIPs.filter(existingIp => existingIp !== ip);
        recentIPs.push(ip);

        if (recentIPs.length > 10) {
            recentIPs = recentIPs.slice(-10);
        }

        localStorage.setItem(RECENT_IPS_KEY, JSON.stringify(recentIPs));
    };

    // Функция для принудительной очистки черного списка от исключенных IP
    const forceCleanBlacklist = () => {
        let blacklist = getBlacklist();
        const originalLength = blacklist.length;

        const cleanedBlacklist = blacklist.filter(ip => {
            if (isExcluded(ip)) {
                return false;
            }
            return isValidIP(ip);
        });

        if (cleanedBlacklist.length !== originalLength) {
            saveBlacklist(cleanedBlacklist);
        }

        return cleanedBlacklist;
    };

    // Функция для отображения сообщения
    const showMessage = (text, backgroundColor = 'red', textColor = 'white') => {
        document.querySelectorAll('.blocker-message').forEach(msg => msg.remove());

        const message = document.createElement('div');
        message.className = 'blocker-message';
        Object.assign(message.style, {
            position: 'fixed',
            bottom: '20px',
            left: '20px',
            padding: '15px 25px',
            backgroundColor: backgroundColor,
            color: textColor,
            borderRadius: '5px',
            fontSize: '18px',
            fontWeight: 'bold',
            zIndex: 10000,
            boxShadow: '0 4px 8px rgba(0,0,0,0.3)',
            transition: 'opacity 0.5s ease-out'
        });
        message.textContent = text;

        document.body.appendChild(message);

        setTimeout(() => {
            message.style.opacity = '0';
            setTimeout(() => {
                if (message.parentNode) {
                    message.parentNode.removeChild(message);
                }
            }, 500);
        }, 6000);
    };

    // Функция для извлечения IP из ICE кандидата
    const extractIPFromCandidate = (candidate) => {
        const regex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/;
        const match = candidate.match(regex);
        return match ? match[0] : null;
    };

    // Функция для извлечения IPv4 из IPv6 адресов
    const extractIPv4FromIPv6 = (ipv6) => {
        const ipv4MappedRegex = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/;
        const mappedMatch = ipv6.match(ipv4MappedRegex);
        if (mappedMatch) return mappedMatch[1];

        const ipv4CompatRegex = /^::(\d+\.\d+\.\d+\.\d+)$/;
        const compatMatch = ipv6.match(ipv4CompatRegex);
        if (compatMatch) return compatMatch[1];

        const nat64Regex = /^64:ff9b::(\d+\.\d+\.\d+\.\d+)$/;
        const nat64Match = ipv6.match(nat64Regex);
        if (nat64Match) return nat64Match[1];

        return null;
    };

    // Функция проверки блокировки IP
    const shouldBlockIP = (ip) => {
        if (!ip || typeof ip !== 'string') {
            return false;
        }

        // Сначала проверяем исключения
        if (isExcluded(ip)) {
            return false;
        }

        const blacklist = getBlacklist();

        if (!ip.includes(':')) {
            return blacklist.includes(ip);
        }

        const extractedIPv4 = extractIPv4FromIPv6(ip);
        if (extractedIPv4) {
            return blacklist.includes(extractedIPv4);
        }

        return false;
    };

    // Функция для поиска и клика по кнопке ДАЛЬШЕ
    const clickNextButton = () => {
        // Основной селектор для кнопки ДАЛЬШЕ на chatroulett.ru
        const nextButton = document.querySelector('button.btn.btn-primary.btn-lg.d-block.w-100.fs-2.px-0.py-3.py-lg-5');
        if (nextButton) {
            if (nextButton.disabled) {
                nextButton.disabled = false;
            }
            nextButton.click();
            return true;
        }

        // Альтернативные селекторы для кнопки ДАЛЬШЕ
        const alternativeSelectors = [
            'button[class*="next"]',
            'button[class*="primary"]',
            '.next-btn',
            '[class*="next"]'
        ];

        for (const selector of alternativeSelectors) {
            const button = document.querySelector(selector);
            if (button) {
                if (button.disabled) {
                    button.disabled = false;
                }
                button.click();
                return true;
            }
        }

        return false;
    };

    // Наблюдатель за кнопкой ДАЛЬШЕ - снимает disabled если он появляется
    const observeNextButton = () => {
        const observer = new MutationObserver(() => {
            const nextButton = document.querySelector('button.btn.btn-primary.btn-lg.d-block.w-100.fs-2.px-0.py-3.py-lg-5');
            if (nextButton && nextButton.disabled) {
                nextButton.disabled = false;
            }

            const alternativeButtons = document.querySelectorAll('button[class*="next"], button[class*="primary"]');
            alternativeButtons.forEach(button => {
                if (button.disabled) {
                    button.disabled = false;
                }
            });
        });

        observer.observe(document.documentElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['disabled', 'class']
        });

        return observer;
    };

    // Функция анализа SDP
    const analyzeSDP = (sdp) => {
        const foundIPs = new Set();
        if (!sdp) return foundIPs;

        const ipv4Regex = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g;

        const matches = sdp.match(ipv4Regex);
        if (matches) {
            matches.forEach(ip => {
                if (isValidIP(ip) && !isExcluded(ip)) {
                    foundIPs.add(ip);
                }
            });
        }

        return foundIPs;
    };

    // Создание кнопки блокировки
    const createButton = () => {
        const banButton = document.createElement('button');
        Object.assign(banButton.style, {
            position: 'fixed',
            top: '50px',
            left: '45px',
            width: '120px',
            height: '120px',
            borderRadius: '50%',
            backgroundColor: 'red',
            color: 'white',
            border: 'none',
            cursor: 'pointer',
            zIndex: 9999,
            fontSize: '12px',
            fontWeight: 'bold',
            boxShadow: '0 4px 8px rgba(0,0,0,0.3)'
        });
        banButton.textContent = 'BAN';
        banButton.title = 'Заблокировать текущего собеседника';
        document.body.appendChild(banButton);
        return banButton;
    };

    // === Перехват RTCPeerConnection выполняется сразу на document-start ===
    let activeConnections = [];

    const OriginalRTCPeerConnection = window.RTCPeerConnection ||
        window.webkitRTCPeerConnection ||
        window.mozRTCPeerConnection;

    if (OriginalRTCPeerConnection) {
        const createBlockedError = (ip) => {
            const error = new Error(`Connection blocked: IP ${ip} in blacklist`);
            error.name = 'BlockedConnectionError';
            return error;
        };

        window.RTCPeerConnection = function(...args) {
            const pc = new OriginalRTCPeerConnection(...args);

            // Перехват setRemoteDescription
            const originalSetRemoteDescription = pc.setRemoteDescription.bind(pc);
            pc.setRemoteDescription = function(description) {
                if (description && description.sdp) {
                    const foundIPs = analyzeSDP(description.sdp);
                    for (const ip of foundIPs) {
                        if (shouldBlockIP(ip)) {
                            showMessage('Блокировка: ' + ip, 'orange', 'white');
                            pc.close();
                            setTimeout(clickNextButton, 100);
                            return Promise.reject(createBlockedError(ip));
                        }
                    }
                }
                return originalSetRemoteDescription(description);
            };

            // Перехват addIceCandidate
            const originalAddIceCandidate = pc.addIceCandidate.bind(pc);
            pc.addIceCandidate = function(candidate) {
                if (candidate && candidate.candidate) {
                    const ip = extractIPFromCandidate(candidate.candidate);
                    if (ip && shouldBlockIP(ip)) {
                        showMessage('Блокировка: ' + ip, 'orange', 'white');
                        pc.close();
                        setTimeout(clickNextButton, 100);
                        return Promise.reject(createBlockedError(ip));
                    }
                }
                return originalAddIceCandidate(candidate);
            };

            // Отслеживание активных соединений
            pc.addEventListener('connectionstatechange', () => {
                if (['closed', 'disconnected', 'failed'].includes(pc.connectionState)) {
                    const index = activeConnections.indexOf(pc);
                    if (index !== -1) activeConnections.splice(index, 1);
                }
            });

            activeConnections.push(pc);
            return pc;
        };

        // Копируем прототип
        Object.setPrototypeOf(window.RTCPeerConnection, OriginalRTCPeerConnection);
        window.RTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype;
    }

    // === Инициализация UI после загрузки DOM ===
    const initUI = () => {
        // Принудительно очищаем черный список от исключенных IP
        forceCleanBlacklist();

        const banButton = createButton();
        const nextButtonObserver = observeNextButton();

        // Обработчик клика по кнопке блокировки
        banButton.addEventListener('click', () => {
            let currentBlacklist = getBlacklist();
            let foundIPs = new Set();
            let alreadyBlockedIPs = new Set();
            let newBlockedIPs = [];

            // Анализ активных соединений
            for (const pc of activeConnections) {
                try {
                    if (pc.remoteDescription && pc.remoteDescription.sdp) {
                        const sdpIPs = analyzeSDP(pc.remoteDescription.sdp);
                        sdpIPs.forEach(ip => {
                            if (shouldBlockIP(ip)) {
                                alreadyBlockedIPs.add(ip);
                            } else if (!isExcluded(ip)) {
                                foundIPs.add(ip);
                            }
                        });
                    }
                } catch (e) {
                    // Игнорируем ошибки анализа
                }
            }

            // Добавление новых IP в черный список
            let addedCount = 0;
            if (foundIPs.size > 0) {
                foundIPs.forEach(ip => {
                    if (!currentBlacklist.includes(ip)) {
                        currentBlacklist.push(ip);
                        newBlockedIPs.push(ip);
                        addedCount++;
                        addToRecentIPs(ip);
                    }
                });

                if (addedCount > 0) {
                    saveBlacklist(currentBlacklist);

                    if (newBlockedIPs.length > 0) {
                        showMessage('Заблокировано: ' + newBlockedIPs.join(', '), 'red', 'white');
                    }

                    // Закрываем все активные соединения
                    activeConnections.forEach(pc => pc.close());
                    activeConnections = [];
                }
            }

            // Обработка уже заблокированных IP
            if (alreadyBlockedIPs.size > 0 && addedCount === 0) {
                const blockedList = Array.from(alreadyBlockedIPs);
                if (blockedList.length > 0) {
                    showMessage('Уже заблокировано: ' + blockedList.join(', '), 'orange', 'white');
                }

                // Закрываем все активные соединения
                activeConnections.forEach(pc => pc.close());
                activeConnections = [];
            }

            // Если не нашли IP вообще
            if (foundIPs.size === 0 && alreadyBlockedIPs.size === 0) {
                showMessage('IP не найден — соединение ещё не установлено', 'gray', 'white');
            }

            // Переход к следующему собеседнику
            clickNextButton();
        });
    };

    // Запуск UI-инициализации после загрузки DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initUI);
    } else {
        initUI();
    }
})();

Chatroulett WebRTC Blocker — техническая документация

Техническая архитектура

1. Слой перехвата WebRTC соединений

  • Monkey-patching RTCPeerConnection — замена глобального конструктора RTCPeerConnection на обёртку, которая перехватывает создание всех новых соединений
  • Перехват выполняется на этапе document-start, до загрузки DOM, чтобы гарантировать перехват всех соединений, создаваемых страницей
  • Двухфазная блокировка:
    • Pre-connection: анализ SDP в setRemoteDescription() до установки соединения
    • Mid-connection: проверка IP в addIceCandidate() во время ICE negotiation
  • Каждое созданное соединение добавляется в массив activeConnections и удаляется из него при переходе в состояние closed, disconnected или failed

2. Система обнаружения IP-адресов

Анализ SDP (Session Description Protocol)

  • Функция analyzeSDP() выполняет полнотекстовый поиск IPv4-адресов во всём теле SDP с помощью регулярного выражения
  • Извлекаются только валидные и не исключённые адреса (проверка через isValidIP() и isExcluded())

Обработка ICE кандидатов

  • Функция extractIPFromCandidate() извлекает первый IPv4-адрес из строки ICE-кандидата
  • Перехват происходит в обёртке addIceCandidate() — при обнаружении заблокированного IP соединение немедленно закрывается

3. Валидация и классификация IP-адресов

Валидация IPv4:

  • Проверка формата xxx.xxx.xxx.xxx (каждый октет 0–255) через регулярное выражение
  • Дополнительная проверка на отсутствие посторонних символов (допускаются только 0-9, a-f, A-F, ., :)

Обработка IPv6:

  • Базовая валидация полного и сокращённого форматов IPv6
  • IPv4-mapped IPv6: ::ffff:192.168.1.1 → извлечение IPv4-компонента
  • IPv4-compatible IPv6: ::192.168.1.1 → конвертация в чистый IPv4
  • NAT64 трансляция: 64:ff9b::192.168.1.1 → декомпозиция IPv4-части
  • Чистые IPv6 без IPv4-компонента → игнорируются (блокировка не применяется)

4. Система управления чёрными списками

Хранение:

  • Чёрный список хранится в localStorage под ключом webrtc_blacklist (массив IP-адресов в формате JSON)
  • Список последних обнаруженных IP хранится под ключом webrtc_recent_ips (до 10 записей)

Очистка при запуске:

  • Функция forceCleanBlacklist() при инициализации удаляет из чёрного списка невалидные и исключённые IP-адреса

CIDR-нотация для диапазонов исключений:

const EXCLUDED_IP_RANGES = [
    '0.0.0.0/8',
    '10.0.0.0/8',
    '127.0.0.0/8',
    '172.16.0.0/12',
    '192.168.0.0/16',
    '93.189.228.0/22',  // подсеть хостинга видеочата
    // ...
];

Алгоритм проверки диапазонов:

  • Преобразование IP и адреса сети в 32-битные числа
  • Формула преобразования: octet1 × 256³ + octet2 × 256² + octet3 × 256 + octet4
  • Битовая маска: ~((1 << (32 - prefix)) - 1) >>> 0
  • Проверка принадлежности: (IP & mask) === (network & mask)

5. Многоуровневая система исключений

Иерархия проверок в функции isExcluded():

  1. Специальные адреса: 0.0.0.0, ::, 255.255.255.255
  2. Префиксные проверки: 192.0.2.*, 198.51.100.*, 203.0.113.*, 169.254.*
  3. Доменные исключения: chatroulett.ru, api.chatroulett.ru, webrtc.chatroulett.ru (для нечисловых значений)
  4. CIDR-диапазоны: private-сети, loopback, link-local, multicast, зарезервированные, подсеть хостинга

6. Механизм наблюдения за интерфейсом

  • MutationObserver отслеживает изменения childList, subtree и атрибутов disabled/class на document.documentElement
  • Автоматически снимает атрибут disabled с кнопки "ДАЛЬШЕ"
  • Поиск кнопки по основному CSS-селектору (button.btn.btn-primary.btn-lg.d-block.w-100.fs-2.px-0.py-3.py-lg-5) и альтернативным селекторам

Визуальный интерфейс

Кнопка блокировки:

  • Красная круглая кнопка "BAN" (120×120px), фиксированная позиция в левом верхнем углу
  • При клике анализирует все активные WebRTC-соединения, добавляет найденные IP в чёрный список и переключает на следующего собеседника

Системные уведомления:

  • Всплывающие сообщения в левом нижнем углу экрана
  • Цветовая кодировка:
    • Оранжевый — автоматическая блокировка при обнаружении IP из чёрного списка (сообщение: Блокировка: IP_адрес)
    • Оранжевый — IP уже находится в чёрном списке (сообщение: Уже заблокировано: IP1, IP2)
    • Красный — успешное добавление нового IP в чёрный список (сообщение: Заблокировано: IP1, IP2)
    • Серый — IP не найден, соединение ещё не установлено
  • Плавное исчезновение через 6 секунд

Пользовательские сценарии:

Автоматическая работа:

  1. Пользователь заходит в видеочат
  2. При установке WebRTC-соединения перехватываются SDP и ICE-кандидаты
  3. Если обнаружен IP из чёрного списка:
    • Оранжевое уведомление: Блокировка: IP_адрес
    • Мгновенное закрытие соединения (pc.close())
    • Автоматическое нажатие кнопки "ДАЛЬШЕ" через 100 мс
    • Поиск нового собеседника

Ручное управление:

  1. Пользователь нажимает кнопку "BAN"
  2. Система анализирует remoteDescription.sdp всех активных соединений
  3. Извлекает IP-адреса собеседника (с фильтрацией исключённых)
  4. Добавляет новые IP в чёрный список и в список последних IP
  5. Показывает: Заблокировано: IP1, IP2
  6. Закрывает все активные соединения
  7. Автоматически нажимает кнопку "ДАЛЬШЕ"

Критические технические особенности

  • Перехват на document-start — RTCPeerConnection патчится до загрузки DOM, гарантируя перехват всех соединений
  • Нулевая задержка блокировки — соединения прерываются на этапе SDP/ICE, до установки медиапотока
  • Отказоустойчивость — ошибки при анализе соединений перехватываются try/catch без нарушения работы чата
  • Автоочистка чёрного списка — при каждом запуске из списка удаляются невалидные и исключённые адреса
  • Поддержка IPv6-to-IPv4 трансляции — IPv4-mapped, IPv4-compatible и NAT64 адреса конвертируются в IPv4 для проверки; чистые IPv6 игнорируются

Процесс принятия решения о блокировке

  1. Извлечение IP-адреса из SDP или ICE-кандидата
  2. Валидация формата (IPv4 или IPv6)
  3. Проверка исключений — специальные адреса, домены, CIDR-диапазоны
  4. Для IPv6 — попытка извлечь IPv4-компонент; при неудаче — пропуск
  5. Поиск в чёрном списке (localStorage)
  6. При совпадении — закрытие соединения + автоматический переход к следующему собеседнику

Необходимые требования

Поддерживаемые браузеры:

  • Google Chrome
  • Mozilla Firefox
  • Microsoft Edge
  • Opera
  • Brave
  • Яндекс.Браузер

Требуемое расширение:

  • Tampermonkey (рекомендуется)
  • Violentmonkey (альтернатива)

Пошаговая инструкция установки

1. Установка менеджера пользовательских скриптов

Для Chrome/Edge/Brave/Яндекс:

  • Откройте Chrome Web Store
  • Найдите "Tampermonkey"
  • Нажмите "Установить"
  • Подтвердите установку расширения

Для Firefox:

  • Откройте Firefox Add-ons
  • Найдите "Tampermonkey"
  • Нажмите "Add to Firefox"
  • Подтвердите установку

2. Установка скрипта WebRTC Blocker

  • Скопируйте полный код скрипта
  • Откройте панель Tampermonkey
  • Создайте новый скрипт ("Добавить новый скрипт")
  • Вставьте код в редактор
  • Нажмите "Файл" → "Сохранить"

3. Проверка установки

Признаки успешной установки:

  • На странице видеочата появляется красная кнопка "BAN" в левом верхнем углу
  • В консоли браузера (F12) видно сообщение: Chatroulett WebRTC Blocker initialized
  • Скрипт отображается как активный в панели Tampermonkey
  • При блокировке появляются цветные уведомления в левом нижнем углу

Комментарии (338)

Правила комментирования
  • Любые темы про политику, войны, или всё то, что НЕ относится к теме сайта, будут УДАЛЯТЬСЯ.
  • Всё, что попадает под возможные нарушения законодательства РФ (экстремизм, призывы, дискредитация, оправдание, возбуждение и т.п.) - тоже.
  • Любые бессмысленные оскорбления участников сайта или тематики сайта. Если с чем-то не согласны - приводите аргументацию, а не оскорбления.
  • Запрещается упоминание в негативном контексте (клевета) каких-либо персоналий - физических или юридических лиц.

Мы стараемся сделать комментарии ценным информационным материалом, засорять сайт мусорными обсуждениями, никак не относящимися к теме сайта, не нужно! Спасибо за понимание.

0 / 10000

Форматы: JPG, PNG, WebP. Не более 5 файлов (по 10 МБ). Можно вставить из буфера (Ctrl+V).

Загрузка комментариев…