From 86a6db1aa5a5e77038d2f0a8cb69f10cf5b7313f Mon Sep 17 00:00:00 2001 From: "V.O.I.D" Date: Wed, 15 Apr 2026 17:14:47 +0000 Subject: [PATCH] Delete parser.py --- parser.py | 924 ------------------------------------------------------ 1 file changed, 924 deletions(-) delete mode 100644 parser.py diff --git a/parser.py b/parser.py deleted file mode 100644 index 68d1390..0000000 --- a/parser.py +++ /dev/null @@ -1,924 +0,0 @@ -import asyncio -import aiohttp -import aiofiles -import re -import os -import time -import json -import subprocess -import tempfile -import requests -import threading -import hashlib -import socket -import random -import urllib.parse -import ssl -import sys -from datetime import datetime, timedelta -from pathlib import Path -from concurrent.futures import ThreadPoolExecutor, as_completed - -# ========= ДИАГНОСТИКА ========= -print(f"🚀 Запуск мультиканального парсера [V.O.I.D]...") -print(f"📂 Текущая директория: {os.getcwd()}") -print(f"🐍 Python версия: {sys.version}") - -# ========= ФАЙЛЫ ========= -SOURCES_FILE = "sources.txt" -OUTPUT_FILE = "url.txt" -CLEAN_FILE = "url_clean.txt" -FILTERED_FILE = "url_filtered.txt" -NAMED_FILE = "url_named.txt" -ENCODED_FILE = "url_encoded.txt" -WORK_FILE = "configs/url_work.txt" -LOG_FILE = "log.txt" -PROCESSED_FILE = "processed.json" -CACHE_FILE = "cache_results.json" -DEBUG_FILE = "debug_failed.txt" -XRAY_LOG_FILE = "xray_errors.log" - -# ========= НАСТРОЙКИ ========= -THREADS_DOWNLOAD = 50 -CYCLE_DELAY = 3600 -LOG_CLEAN_INTERVAL = 86400 -CYCLES_BEFORE_DEBUG_CLEAN = 5 - -XRAY_MAX_WORKERS = 30 -XRAY_TEST_URL = "https://www.gstatic.com/generate_204" -XRAY_TIMEOUT = 3 -MAX_RETRIES = 2 -RETRY_DELAY = 1 - -print(f"⚡ Настройки: XRAY_MAX_WORKERS={XRAY_MAX_WORKERS}, TIMEOUT={XRAY_TIMEOUT}") - -cycle_counter = 0 - -# ========= РЕГУЛЯРКИ (ОБНОВЛЕНО: VLESS, TROJAN, HY2) ========= -# Используем не захватывающую группу для протоколов -CONFIG_REGEX = re.compile(r"(?:vless|trojan|hysteria2|hy2)://[^\s\"'<]+", re.IGNORECASE) -UUID_REGEX = re.compile( - r"[0-9a-fA-F]{8}-" - r"[0-9a-fA-F]{4}-" - r"[0-9a-fA-F]{4}-" - r"[0-9a-fA-F]{4}-" - r"[0-9a-fA-F]{12}" -) - -# ========= СЛОВАРЬ ДОМЕНОВ (RU-SEGMENT) ========= -DOMAIN_NAMES = { - 'x5.ru': 'Пятёрочка', '5ka.ru': 'Пятёрочка', '5ka-cdn.ru': 'Пятёрочка', - '5ka.static.ru': 'Пятёрочка', 'ads.x5.ru': 'Пятёрочка', 'perekrestok.ru': 'Перекрёсток', - 'vprok.ru': 'Перекрёсток', 'dixy.ru': 'Дикси', 'fasssst.ru': 'Fasssst', - 'rontgen.fasssst.ru': 'Fasssst', 'res.fasssst.ru': 'Fasssst', 'yt.fasssst.ru': 'Fasssst', - 'fast.strelkavpn.ru': 'StrelkaVPN', 'strelkavpn.ru': 'StrelkaVPN', 'maviks.ru': 'Maviks', - 'ru.maviks.ru': 'Maviks', 'a.ru.maviks.ru': 'Maviks', 'tree-top.cc': 'TreeTop', - 'a.ru.tree-top.cc': 'TreeTop', 'connect-iskra.ru': 'Iskra', '212-wl.connect-iskra.ru': 'Iskra', - 'vk.com': 'VK', 'vk.ru': 'VK', 'vkontakte.ru': 'VK', 'userapi.com': 'VK', - 'cdn.vk.com': 'VK', 'cdn.vk.ru': 'VK', 'id.vk.com': 'VK', 'id.vk.ru': 'VK', - 'login.vk.com': 'VK', 'login.vk.ru': 'VK', 'api.vk.com': 'VK', 'api.vk.ru': 'VK', - 'im.vk.com': 'VK', 'm.vk.com': 'VK', 'm.vk.ru': 'VK', 'sun6-22.userapi.com': 'VK', - 'sun6-21.userapi.com': 'VK', 'sun6-20.userapi.com': 'VK', 'sun9-38.userapi.com': 'VK', - 'sun9-101.userapi.com': 'VK', 'pptest.userapi.com': 'VK', 'vk-portal.net': 'VK', - 'stats.vk-portal.net': 'VK', 'akashi.vk-portal.net': 'VK', 'vkvideo.ru': 'VK Видео', - 'm.vkvideo.ru': 'VK Видео', 'queuev4.vk.com': 'VK', 'eh.vk.com': 'VK', 'cloud.vk.com': 'VK', - 'cloud.vk.ru': 'VK', 'admin.cs7777.vk.ru': 'VK', 'admin.tau.vk.ru': 'VK', - 'analytics.vk.ru': 'VK', 'api.cs7777.vk.ru': 'VK', 'api.tau.vk.ru': 'VK', - 'away.cs7777.vk.ru': 'VK', 'away.tau.vk.ru': 'VK', 'business.vk.ru': 'VK', - 'connect.cs7777.vk.ru': 'VK', 'cs7777.vk.ru': 'VK', 'dev.cs7777.vk.ru': 'VK', - 'dev.tau.vk.ru': 'VK', 'expert.vk.ru': 'VK', 'id.cs7777.vk.ru': 'VK', 'id.tau.vk.ru': 'VK', - 'login.cs7777.vk.ru': 'VK', 'login.tau.vk.ru': 'VK', 'm.cs7777.vk.ru': 'VK', - 'm.tau.vk.ru': 'VK', 'm.vkvideo.cs7777.vk.ru': 'VK Видео', 'me.cs7777.vk.ru': 'VK', - 'ms.cs7777.vk.ru': 'VK', 'music.vk.ru': 'VK Музыка', 'oauth.cs7777.vk.ru': 'VK', - 'oauth.tau.vk.ru': 'VK', 'oauth2.cs7777.vk.ru': 'VK', 'ord.vk.ru': 'VK', 'push.vk.ru': 'VK', - 'r.vk.ru': 'VK', 'target.vk.ru': 'VK', 'tech.vk.ru': 'VK', 'ui.cs7777.vk.ru': 'VK', - 'ui.tau.vk.ru': 'VK', 'vkvideo.cs7777.vk.ru': 'VK Видео', 'speedload.ru': 'Speedload', - 'api.speedload.ru': 'Speedload', 'chat.speedload.ru': 'Speedload', 'serverstats.ru': 'ServerStats', - 'cdnfive.serverstats.ru': 'ServerStats', 'cdncloudtwo.serverstats.ru': 'ServerStats', - 'furypay.ru': 'FuryPay', 'api.furypay.ru': 'FuryPay', 'jojack.ru': 'JoJack', - 'spb.jojack.ru': 'JoJack', 'at.jojack.ru': 'JoJack', 'tcp-reset-club.net': 'TCP Reset', - 'est01-ss01.tcp-reset-club.net': 'TCP Reset', 'nl01-ss01.tcp-reset-club.net': 'TCP Reset', - 'ru01-blh01.tcp-reset-club.net': 'TCP Reset', 'gov.ru': 'Госуслуги', 'kremlin.ru': 'Кремль', - 'government.ru': 'Правительство', 'duma.gov.ru': 'Госдума', 'genproc.gov.ru': 'Генпрокуратура', - 'epp.genproc.gov.ru': 'Генпрокуратура', 'cikrf.ru': 'ЦИК', 'izbirkom.ru': 'Избирком', - 'gosuslugi.ru': 'Госуслуги', 'sfd.gosuslugi.ru': 'Госуслуги', 'esia.gosuslugi.ru': 'Госуслуги', - 'bot.gosuslugi.ru': 'Госуслуги', 'contract.gosuslugi.ru': 'Госуслуги', - 'novorossiya.gosuslugi.ru': 'Госуслуги', 'pos.gosuslugi.ru': 'Госуслуги', - 'lk.gosuslugi.ru': 'Госуслуги', 'map.gosuslugi.ru': 'Госуслуги', - 'partners.gosuslugi.ru': 'Госуслуги', 'gosweb.gosuslugi.ru': 'Госуслуги', - 'voter.gosuslugi.ru': 'Госуслуги', 'gu-st.ru': 'Госуслуги', 'nalog.ru': 'ФНС', - 'pfr.gov.ru': 'ПФР', 'digital.gov.ru': 'Минцифры', 'adm.digital.gov.ru': 'Минцифры', - 'xn--80ajghhoc2aj1c8b.xn--p1ai': 'Минцифры', 'a.res-nsdi.ru': 'NSDI', - 'b.res-nsdi.ru': 'NSDI', 'a.auth-nsdi.ru': 'NSDI', 'b.auth-nsdi.ru': 'NSDI', - 'ok.ru': 'Одноклассники', 'odnoklassniki.ru': 'Одноклассники', 'cdn.ok.ru': 'Одноклассники', - 'st.okcdn.ru': 'Одноклассники', 'st.ok.ru': 'Одноклассники', 'apiok.ru': 'Одноклассники', - 'jira.apiok.ru': 'Одноклассники', 'api.ok.ru': 'Одноклассники', 'm.ok.ru': 'Одноклассники', - 'live.ok.ru': 'Одноклассники', 'multitest.ok.ru': 'Одноклассники', 'dating.ok.ru': 'Одноклассники', - 'tamtam.ok.ru': 'Одноклассники', '742231.ms.ok.ru': 'Одноклассники', 'ozon.ru': 'Ozon', - 'www.ozon.ru': 'Ozon', 'seller.ozon.ru': 'Ozon', 'bank.ozon.ru': 'Ozon', 'pay.ozon.ru': 'Ozon', - 'securepay.ozon.ru': 'Ozon', 'adv.ozon.ru': 'Ozon', 'invest.ozon.ru': 'Ozon', - 'ord.ozon.ru': 'Ozon', 'autodiscover.ord.ozon.ru': 'Ozon', 'st.ozone.ru': 'Ozon', - 'ir.ozone.ru': 'Ozon', 'vt-1.ozone.ru': 'Ozon', 'ir-2.ozone.ru': 'Ozon', 'xapi.ozon.ru': 'Ozon', - 'owa.ozon.ru': 'Ozon', 'learning.ozon.ru': 'Ozon', 'mapi.learning.ozon.ru': 'Ozon', - 'ws.seller.ozon.ru': 'Ozon', 'wildberries.ru': 'Wildberries', 'wb.ru': 'Wildberries', - 'static.wb.ru': 'Wildberries', 'seller.wildberries.ru': 'Wildberries', - 'banners.wildberries.ru': 'Wildberries', 'fw.wb.ru': 'Wildberries', 'finance.wb.ru': 'Wildberries', - 'jitsi.wb.ru': 'Wildberries', 'dnd.wb.ru': 'Wildberries', 'user-geo-data.wildberries.ru': 'Wildberries', - 'banners-website.wildberries.ru': 'Wildberries', 'chat-prod.wildberries.ru': 'Wildberries', - 'a.wb.ru': 'Wildberries', 'avito.ru': 'Avito', 'm.avito.ru': 'Avito', 'api.avito.ru': 'Avito', - 'avito.st': 'Avito', 'img.avito.st': 'Avito', 'sntr.avito.ru': 'Avito', 'stats.avito.ru': 'Avito', - 'cs.avito.ru': 'Avito', 'www.avito.st': 'Avito', 'st.avito.ru': 'Avito', 'www.avito.ru': 'Avito', - **{f'{i:02d}.img.avito.st': 'Avito' for i in range(100)}, - 'sberbank.ru': 'Сбербанк', 'online.sberbank.ru': 'Сбербанк', 'sber.ru': 'Сбербанк', - 'id.sber.ru': 'Сбербанк', 'bfds.sberbank.ru': 'Сбербанк', 'cms-res-web.online.sberbank.ru': 'Сбербанк', - 'esa-res.online.sberbank.ru': 'Сбербанк', 'pl-res.online.sberbank.ru': 'Сбербанк', - 'www.sberbank.ru': 'Сбербанк', 'vtb.ru': 'ВТБ', 'www.vtb.ru': 'ВТБ', 'online.vtb.ru': 'ВТБ', - 'chat3.vtb.ru': 'ВТБ', 's.vtb.ru': 'ВТБ', 'sso-app4.vtb.ru': 'ВТБ', 'sso-app5.vtb.ru': 'ВТБ', - 'gazprombank.ru': 'Газпромбанк', 'alfabank.ru': 'Альфа-Банк', 'metrics.alfabank.ru': 'Альфа-Банк', - 'tinkoff.ru': 'Тинькофф', 'tbank.ru': 'Тинькофф', 'cdn.tbank.ru': 'Тинькофф', 'hrc.tbank.ru': 'Тинькофф', - 'cobrowsing.tbank.ru': 'Тинькофф', 'le.tbank.ru': 'Тинькофф', 'id.tbank.ru': 'Тинькофф', - 'imgproxy.cdn-tinkoff.ru': 'Тинькофф', 'banki.ru': 'Банки.ру', 'yandex.ru': 'Яндекс', - 'ya.ru': 'Яндекс', 'dzen.ru': 'Дзен', 'kinopoisk.ru': 'Кинопоиск', 'yastatic.net': 'Яндекс', - 'yandex.net': 'Яндекс', 'mail.yandex.ru': 'Яндекс Почта', 'disk.yandex.ru': 'Яндекс Диск', - 'maps.yandex.ru': 'Яндекс Карты', 'api-maps.yandex.ru': 'Яндекс Карты', - 'enterprise.api-maps.yandex.ru': 'Яндекс Карты', 'music.yandex.ru': 'Яндекс Музыка', - 'yandex.by': 'Яндекс', 'yandex.com': 'Яндекс', 'travel.yandex.ru': 'Яндекс Путешествия', - 'informer.yandex.ru': 'Яндекс', 'mediafeeds.yandex.ru': 'Яндекс', 'mediafeeds.yandex.com': 'Яндекс', - 'uslugi.yandex.ru': 'Яндекс Услуги', 'kiks.yandex.ru': 'Яндекс', 'kiks.yandex.com': 'Яндекс', - 'frontend.vh.yandex.ru': 'Яндекс', 'favicon.yandex.ru': 'Яндекс', 'favicon.yandex.com': 'Яндекс', - 'favicon.yandex.net': 'Яндекс', 'browser.yandex.ru': 'Яндекс Браузер', - 'browser.yandex.com': 'Яндекс Браузер', 'api.browser.yandex.ru': 'Яндекс Браузер', - 'api.browser.yandex.com': 'Яндекс Браузер', 'wap.yandex.ru': 'Яндекс', 'wap.yandex.com': 'Яндекс', - '300.ya.ru': 'Яндекс', 'brontp-pre.yandex.ru': 'Яндекс', 'suggest.dzen.ru': 'Дзен', - 'suggest.sso.dzen.ru': 'Дзен', 'sso.dzen.ru': 'Дзен', 'mail.yandex.com': 'Яндекс Почта', - 'yabs.yandex.ru': 'Яндекс', 'neuro.translate.yandex.ru': 'Яндекс Перевод', 'cdn.yandex.ru': 'Яндекс', - 'zen.yandex.ru': 'Дзен', 'zen.yandex.com': 'Дзен', 'zen.yandex.net': 'Дзен', - 'collections.yandex.ru': 'Яндекс Коллекции', 'collections.yandex.com': 'Яндекс Коллекции', - 'an.yandex.ru': 'Яндекс', 'sba.yandex.ru': 'Яндекс', 'sba.yandex.com': 'Яндекс', - 'sba.yandex.net': 'Яндекс', 'surveys.yandex.ru': 'Яндекс Опросы', - 'yabro-wbplugin.edadeal.yandex.ru': 'Яндекс', 'api.events.plus.yandex.net': 'Яндекс Плюс', - 'speller.yandex.net': 'Яндекс Спеллер', 'avatars.mds.yandex.net': 'Яндекс', - 'avatars.mds.yandex.com': 'Яндекс', 'mc.yandex.ru': 'Яндекс', 'mc.yandex.com': 'Яндекс', - '3475482542.mc.yandex.ru': 'Яндекс', 'zen-yabro-morda.mediascope.mc.yandex.ru': 'Яндекс', - 'travel.yastatic.net': 'Яндекс', 'api.uxfeedback.yandex.net': 'Яндекс', - 'api.s3.yandex.net': 'Яндекс', 'cdn.s3.yandex.net': 'Яндекс', - 'uxfeedback-cdn.s3.yandex.net': 'Яндекс', 'uxfeedback.yandex.ru': 'Яндекс', - 'cloudcdn-m9-15.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-14.cdn.yandex.net': 'Яндекс', - 'cloudcdn-m9-13.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-12.cdn.yandex.net': 'Яндекс', - 'cloudcdn-m9-10.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-9.cdn.yandex.net': 'Яндекс', - 'cloudcdn-m9-7.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-6.cdn.yandex.net': 'Яндекс', - 'cloudcdn-m9-5.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-4.cdn.yandex.net': 'Яндекс', - 'cloudcdn-m9-3.cdn.yandex.net': 'Яндекс', 'cloudcdn-m9-2.cdn.yandex.net': 'Яндекс', - 'cloudcdn-ams19.cdn.yandex.net': 'Яндекс', 'http-check-headers.yandex.ru': 'Яндекс', - 'cloud.cdn.yandex.net': 'Яндекс', 'cloud.cdn.yandex.com': 'Яндекс', - 'cloud.cdn.yandex.ru': 'Яндекс', 'dr2.yandex.net': 'Яндекс', 'dr.yandex.net': 'Яндекс', - 's3.yandex.net': 'Яндекс', 'static-mon.yandex.net': 'Яндекс', 'sync.browser.yandex.net': 'Яндекс', - 'storage.ape.yandex.net': 'Яндекс', 'strm-rad-23.strm.yandex.net': 'Яндекс', - 'strm.yandex.net': 'Яндекс', 'strm.yandex.ru': 'Яндекс', 'log.strm.yandex.ru': 'Яндекс', - 'egress.yandex.net': 'Яндекс', 'cdnrhkgfkkpupuotntfj.svc.cdn.yandex.net': 'Яндекс', - 'csp.yandex.net': 'Яндекс', 'mail.ru': 'Mail.ru', 'e.mail.ru': 'Mail.ru', 'my.mail.ru': 'Mail.ru', - 'cloud.mail.ru': 'Mail.ru', 'inbox.ru': 'Mail.ru', 'list.ru': 'Mail.ru', 'bk.ru': 'Mail.ru', - 'myteam.mail.ru': 'Mail.ru', 'trk.mail.ru': 'Mail.ru', '1l-api.mail.ru': 'Mail.ru', - 'rutube.ru': 'Rutube', 'static.rutube.ru': 'Rutube', 'rutubelist.ru': 'Rutube', - 'pic.rutubelist.ru': 'Rutube', 'ssp.rutube.ru': 'Rutube', 'preview.rutube.ru': 'Rutube', - 'goya.rutube.ru': 'Rutube', 'smotrim.ru': 'Смотрим', 'ivi.ru': 'Ivi', 'cdn.ivi.ru': 'Ivi', - 'okko.tv': 'Okko', 'start.ru': 'Start', 'wink.ru': 'Wink', 'kion.ru': 'Kion', - 'premier.one': 'Premier', 'more.tv': 'More.tv', 'm.47news.ru': '47 News', - 'lenta.ru': 'Лента.ру', 'gazeta.ru': 'Газета.ру', 'kp.ru': 'Комсомольская Правда', - 'rambler.ru': 'Рамблер', 'ria.ru': 'РИА Новости', 'tass.ru': 'ТАСС', - 'interfax.ru': 'Интерфакс', 'kommersant.ru': 'Коммерсант', 'vedomosti.ru': 'Ведомости', - 'rbc.ru': 'РБК', 'russian.rt.com': 'RT', 'iz.ru': 'Известия', 'mk.ru': 'Московский Комсомолец', - 'rg.ru': 'Российская Газета', 'www.kinopoisk.ru': 'Кинопоиск', 'widgets.kinopoisk.ru': 'Кинопоиск', - 'payment-widget.plus.kinopoisk.ru': 'Кинопоиск', 'external-api.mediabilling.kinopoisk.ru': 'Кинопоиск', - 'external-api.plus.kinopoisk.ru': 'Кинопоиск', 'graphql-web.kinopoisk.ru': 'Кинопоиск', - 'graphql.kinopoisk.ru': 'Кинопоиск', 'tickets.widget.kinopoisk.ru': 'Кинопоиск', - 'st.kinopoisk.ru': 'Кинопоиск', 'quiz.kinopoisk.ru': 'Кинопоиск', - 'payment-widget.kinopoisk.ru': 'Кинопоиск', 'payment-widget-smarttv.plus.kinopoisk.ru': 'Кинопоиск', - 'oneclick-payment.kinopoisk.ru': 'Кинопоиск', 'microapps.kinopoisk.ru': 'Кинопоиск', - 'ma.kinopoisk.ru': 'Кинопоиск', 'hd.kinopoisk.ru': 'Кинопоиск', - 'crowdtest.payment-widget-smarttv.plus.tst.kinopoisk.ru': 'Кинопоиск', - 'crowdtest.payment-widget.plus.tst.kinopoisk.ru': 'Кинопоиск', 'api.plus.kinopoisk.ru': 'Кинопоиск', - 'st-im.kinopoisk.ru': 'Кинопоиск', 'sso.kinopoisk.ru': 'Кинопоиск', 'touch.kinopoisk.ru': 'Кинопоиск', - '2gis.ru': '2ГИС', '2gis.com': '2ГИС', 'api.2gis.ru': '2ГИС', 'keys.api.2gis.com': '2ГИС', - 'tutu.ru': 'Туту.ру', 'img.tutu.ru': 'Туту.ру', 'rzd.ru': 'РЖД', 'ticket.rzd.ru': 'РЖД', - 'cdek.ru': 'СДЭК', 'cdek.market': 'СДЭК', 'calc.cdek.ru': 'СДЭК', 'pochta.ru': 'Почта России', - 'rostelecom.ru': 'Ростелеком', 'rt.ru': 'Ростелеком', 'mts.ru': 'МТС', 'megafon.ru': 'Мегафон', - 'beeline.ru': 'Билайн', 'tele2.ru': 'Tele2', 't2.ru': 'Tele2', 'www.t2.ru': 'Tele2', - 'msk.t2.ru': 'Tele2', 's3.t2.ru': 'Tele2', 'yota.ru': 'Yota', 'domru.ru': 'Дом.ру', - 'ertelecom.ru': 'ЭР-Телеком', 'selectel.ru': 'Selectel', 'timeweb.ru': 'Timeweb', - 'gismeteo.ru': 'Гисметео', 'meteoinfo.ru': 'Метео', 'rp5.ru': 'RП5', 'hh.ru': 'HeadHunter', - 'superjob.ru': 'SuperJob', 'rabota.ru': 'Работа.ру', 'auto.ru': 'Auto.ru', - 'sso.auto.ru': 'Auto.ru', 'drom.ru': 'Drom', 'avto.ru': 'Avto.ru', 'eda.ru': 'Eda.ru', - 'food.ru': 'Food.ru', 'edadeal.ru': 'Edadeal', 'delivery-club.ru': 'Delivery Club', - 'leroymerlin.ru': 'Леруа Мерлен', 'lemanapro.ru': 'Лемана Про', 'cdn.lemanapro.ru': 'Лемана Про', - 'static.lemanapro.ru': 'Лемана Про', 'dmp.dmpkit.lemanapro.ru': 'Лемана Про', - 'receive-sentry.lmru.tech': 'Лемана Про', 'partners.lemanapro.ru': 'Лемана Про', - 'petrovich.ru': 'Петрович', 'maxidom.ru': 'Максидом', 'vseinstrumenti.ru': 'ВсеИнструменты', - '220-volt.ru': '220 Вольт', 'max.ru': 'Max', 'dev.max.ru': 'Max', 'web.max.ru': 'Max', - 'api.max.ru': 'Max', 'legal.max.ru': 'Max', 'st.max.ru': 'Max', 'botapi.max.ru': 'Max', - 'link.max.ru': 'Max', 'download.max.ru': 'Max', 'i.max.ru': 'Max', 'help.max.ru': 'Max', - 'mos.ru': 'Мос.ру', 'taximaxim.ru': 'Такси Максим', 'moskva.taximaxim.ru': 'Такси Максим' -} - -print(f"📋 Загружено {len(DOMAIN_NAMES)} доменов в словарь") - -# ========= ЛОГ ========= -async def log(message: str): - try: - now = datetime.now() - if os.path.exists(LOG_FILE): - mtime = datetime.fromtimestamp(os.path.getmtime(LOG_FILE)) - if now - mtime > timedelta(seconds=LOG_CLEAN_INTERVAL): - open(LOG_FILE, "w").close() - - async with aiofiles.open(LOG_FILE, "a", encoding="utf-8") as f: - await f.write(f"[{now}] {message}\n") - except: - pass - -def log_xray_error(message: str): - try: - with open(XRAY_LOG_FILE, "a", encoding="utf-8") as f: - f.write(f"[{datetime.now().strftime('%H:%M:%S')}] {message}\n") - except: - pass - -# ========= ВАЛИДАЦИЯ (ОБНОВЛЕНО) ========= -def validate_config(url: str) -> bool: - """Проверяет валидность конфига для VLESS, Trojan, Hy2.""" - try: - if url.startswith("vless://"): - return bool(UUID_REGEX.search(url)) and "@" in url and ":" in url - if url.startswith(("trojan://", "hysteria2://", "hy2://")): - # Для трояна и гистерии формат может отличаться от UUID, проверяем наличие структуры - return "@" in url and ":" in url - return False - except: - return False - -# ========= WHITELIST ========= -def load_whitelist_domains(): - domains = set() - suffixes = [] - if os.path.exists("whitelist.txt"): - try: - with open("whitelist.txt", "r", encoding="utf-8", errors="ignore") as f: - for line in f: - d = line.strip().lower() - if d: - domains.add(d) - suffixes.append("." + d) - print(f"📋 Загружено {len(domains)} доменов из whitelist.txt") - except: - print("⚠️ Ошибка загрузки whitelist.txt") - return domains, suffixes - -# ========= ПРОТОКОЛ / SNI / НАЗВАНИЕ (ОБНОВЛЕНО) ========= -def detect_protocol(url: str) -> str: - try: - if url.startswith("hysteria2://") or url.startswith("hy2://"): - return "Hy2" - if url.startswith("trojan://"): - return "Trojan" - - no_scheme = url.split("://", 1)[1] - after_at = no_scheme.split("@", 1)[1] - query = after_at.split("?", 1)[1] if "?" in after_at else "" - params = dict(urllib.parse.parse_qsl(query)) - - transport = params.get("type", "").lower() - security = params.get("security", "").lower() - - if transport in ("ws", "websocket"): return "WS" - if transport in ("grpc", "gun"): return "gRPC" - if transport in ("xhttp", "httpupgrade"): return "XHTTP" - if transport in ("h2", "http2"): return "H2" - if transport == "tcp": return "TCP" - if security == "reality": return "Reality" - if security in ("tls", "xtls"): return "TLS" - return "VLESS" - except: - return "VPN" - -def extract_all_possible_domains(url: str) -> list: - domains = set() - try: - parts = url.split("://", 1) - if len(parts) < 2: return [] - - content = parts[1] - at_pos = content.find('@') - if at_pos == -1: return [] - - after_at = content[at_pos+1:] - q_pos = after_at.find('?') - - if q_pos != -1: - host_part = after_at[:q_pos] - query_part = after_at[q_pos+1:] - else: - host_part = after_at - query_part = "" - - host = host_part.split(':', 1)[0] - if host and '.' in host: - domains.add(host.lower()) - - if query_part: - if '#' in query_part: query_part = query_part.split('#', 1)[0] - params = dict(urllib.parse.parse_qsl(query_part)) - - # Стандартные ключи для SNI и Host - for k in ['sni', 'host', 'peer']: - if k in params and '.' in params[k]: - domains.add(params[k].lower()) - - if 'path' in params: - path_parts = re.findall(r'[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-zA-Z0-9]\.[a-zA-Z]{2,}', params['path']) - domains.update(d.lower() for d in path_parts) - - return list(domains) - except: - return [] - -def get_human_name(domain: str) -> str: - if not domain: return "Неизвестно" - d = domain.lower() - - if d in DOMAIN_NAMES: return DOMAIN_NAMES[d] - - parts = d.split('.') - for i in range(len(parts) - 1): - if ".".join(parts[i:]) in DOMAIN_NAMES: - return DOMAIN_NAMES[".".join(parts[i:])] - - if len(parts) >= 2 and ".".join(parts[-2:]) in DOMAIN_NAMES: - return DOMAIN_NAMES[".".join(parts[-2:])] - - return "Неизвестно" - -def filter_by_sni(url: str, whitelist_domains: set, whitelist_suffixes: list) -> bool: - domains = extract_all_possible_domains(url) - - if whitelist_domains: - for domain in domains: - if domain in whitelist_domains or any(domain.endswith(s) for s in whitelist_suffixes): return True - if len(domain.split('.')) >= 2 and '.'.join(domain.split('.')[-2:]) in whitelist_domains: return True - return False - - for domain in domains: - if domain in DOMAIN_NAMES: return True - parts = domain.split('.') - for i in range(len(parts) - 1): - if ".".join(parts[i:]) in DOMAIN_NAMES: return True - if len(parts) >= 2 and ".".join(parts[-2:]) in DOMAIN_NAMES: return True - - return False - -# ========= СКАЧИВАНИЕ ========= -async def fetch(session, url, sem): - async with sem: - try: - print(f"Скачиваю: {url}") - async with session.get(url, timeout=15) as resp: - if resp.status == 200: - return await resp.text() - except Exception as e: - await log(f"Ошибка при скачивании {url}: {e}") - return None - -async def process_url(session, url, sem, output_queue, stats_lock, stats): - content = await fetch(session, url, sem) - async with stats_lock: - stats["processed"] += 1 - - if content: - matches = CONFIG_REGEX.findall(content) - if matches: - async with stats_lock: - stats["found"] += len(matches) - for match in matches: - await output_queue.put(match) - - async with stats_lock: - print(f"Обработано: {stats['processed']} | Найдено конфигов: {stats['found']}", end="\r") - -async def writer_task(output_queue, output_file): - async with aiofiles.open(output_file, "w", encoding="utf-8") as f: - while True: - line = await output_queue.get() - if line is None: - break - await f.write(line + "\n") - output_queue.task_done() - -# ========= ОЧИСТКА ========= -async def clean_configs(): - print("\nОчищаю дубликаты и проверяю валидность...") - if not os.path.exists(OUTPUT_FILE): return - - try: - async with aiofiles.open(OUTPUT_FILE, "r", encoding="utf-8") as f: - lines = await f.readlines() - except: - return - - unique_valid = {url.strip() for url in lines if url.strip() and validate_config(url.strip())} - - async with aiofiles.open(CLEAN_FILE, "w", encoding="utf-8") as f: - await f.write("\n".join(unique_valid) + "\n") - - print(f"Очистка завершена. Итоговых конфигов: {len(unique_valid)}") - -# ========= ФИЛЬТРАЦИЯ ПО WHITELIST ========= -async def filter_configs(): - print("\n=== Фильтрация по whitelist (V.O.I.D) ===") - if not os.path.exists(CLEAN_FILE): return - - domains, suffixes = load_whitelist_domains() - try: - with open(CLEAN_FILE, "r", encoding="utf-8", errors="ignore") as f: - total = sum(1 for _ in f) - except: - total = 0 - - passed, processed = 0, 0 - - async with aiofiles.open(CLEAN_FILE, "r", encoding="utf-8") as f_in, \ - aiofiles.open(FILTERED_FILE, "w", encoding="utf-8") as f_out: - async for line in f_in: - processed += 1 - url = line.strip() - if not url: continue - - if filter_by_sni(url, domains, suffixes): - await f_out.write(url + "\n") - passed += 1 - - if processed % 100 == 0 and total > 0: - print(f"Фильтрация: {processed}/{total} | Подошло: {passed}", end="\r") - - print(f"\nФильтрация завершена. Итог: {passed} конфигов.") - -# ========= ПЕРЕИМЕНОВАНИЕ (ОБНОВЛЕНО) ========= -async def rename_configs(): - print("\n=== Переименование конфигов по протоколу и SNI ===") - if not os.path.exists(FILTERED_FILE): return - - try: - with open(FILTERED_FILE, "r", encoding="utf-8", errors="ignore") as f: - total = sum(1 for _ in f) - except: - total = 0 - - processed = 0 - - async with aiofiles.open(FILTERED_FILE, "r", encoding="utf-8") as f_in, \ - aiofiles.open(NAMED_FILE, "w", encoding="utf-8") as f_out: - async for line in f_in: - processed += 1 - url = line.strip() - if not url: continue - - protocol = detect_protocol(url) - domains = extract_all_possible_domains(url) - human_name = "Неизвестно" - - if domains: - for domain in domains: - name = get_human_name(domain) - if name != "Неизвестно": - human_name = name - break - if human_name == "Неизвестно" and domains: - human_name = domains[0].split('.')[-2].capitalize() if len(domains[0].split('.')) >= 2 else domains[0] - - title = f"{protocol}, {human_name} [V.O.I.D]" - base = url.split("#", 1)[0] - new_url = f"{base}#{title}" - - await f_out.write(new_url + "\n") - - if processed % 500 == 0 and total > 0: - print(f"Переименовано: {processed}/{total}", end="\r") - - print(f"\nПереименование завершено. Итог: {processed} конфигов.") - -# ========= НОРМАЛИЗАЦИЯ URL (ОБНОВЛЕНО) ========= -def encode_config_url(url: str) -> str: - """Корректное кодирование URL для всех типов протоколов.""" - try: - schemes = ("vless://", "trojan://", "hysteria2://", "hy2://") - current_scheme = next((s for s in schemes if url.startswith(s)), None) - if not current_scheme: return url - - content = url[len(current_scheme):] - at_pos = content.find('@') - if at_pos == -1: return url - - auth = content[:at_pos] - after_at = content[at_pos+1:] - - q_pos = after_at.find('?') - if q_pos != -1: - host_part = after_at[:q_pos] - params_part = after_at[q_pos+1:] - else: - host_part = after_at - params_part = "" - - hash_pos = host_part.find('#') - if hash_pos != -1: - host_only = host_part[:hash_pos] - fragment = host_part[hash_pos+1:] - else: - host_only = host_part - fragment = "" - - if not fragment and params_part: - hash_pos = params_part.find('#') - if hash_pos != -1: - params_only = params_part[:hash_pos] - fragment = params_part[hash_pos+1:] - params_part = params_only - - params = dict(urllib.parse.parse_qsl(params_part, keep_blank_values=True)) - encoded_params = [] - for k, v in params.items(): - if k in ['security', 'type', 'fp', 'pbk', 'sid', 'flow', 'mode']: - encoded_params.append(f"{k}={v}") - else: - encoded_v = urllib.parse.quote(v, safe='%') - encoded_params.append(f"{k}={encoded_v}") - - new_params = "&".join(encoded_params) - - try: - fragment.encode('ascii') - encoded_fragment = fragment - except UnicodeEncodeError: - encoded_fragment = urllib.parse.quote(fragment, safe='') - - base = f"{current_scheme}{auth}@{host_only}" - if new_params: base += f"?{new_params}" - if encoded_fragment: base += f"#{encoded_fragment}" - - return base - - except Exception: - return url - -async def encode_all_configs(): - print("\n=== Кодирование конфигов для Xray ===") - if not os.path.exists(NAMED_FILE): return - - try: - with open(NAMED_FILE, 'r', encoding='utf-8') as f: - configs = [line.strip() for line in f if line.strip()] - except: - return - - total = len(configs) - changed = 0 - - async with aiofiles.open(ENCODED_FILE, "w", encoding="utf-8") as f_out: - for i, url in enumerate(configs, 1): - encoded_url = encode_config_url(url) - await f_out.write(encoded_url + "\n") - if encoded_url != url: changed += 1 - if i % 500 == 0: - print(f"Закодировано: {i}/{total} | Изменено: {changed}", end="\r") - - print(f"\nКодирование завершено. Всего: {total}, изменено: {changed}") - -# ========= ТЕСТОВЫЕ МЕТОДЫ ========= -def check_tcp_connection(host: str, port: int, timeout: int = 2) -> bool: - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(timeout) - result = sock.connect_ex((host, port)) - sock.close() - return result == 0 - except: - return False - -def check_tls_handshake(host: str, port: int = 443, sni: str = None, timeout: int = 2) -> tuple: - try: - context = ssl.create_default_context() - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(timeout) - sock.connect((host, port)) - - ssl_sock = context.wrap_socket(sock, server_hostname=sni or host) - ssl_sock.do_handshake() - version = ssl_sock.version() - ssl_sock.close() - sock.close() - return (True, version, None) - except Exception as e: - return (False, None, str(e)) - -# ========= XRAY-ТЕСТЕР (ОБНОВЛЕНО) ========= -class SimpleProgress: - def __init__(self, total): - self.total = total - self.current = 0 - self.start_time = time.time() - self.lock = threading.Lock() - self.working_count = 0 - self.retry_count = 0 - - def update(self, status='', working=False, retry=False): - with self.lock: - self.current += 1 - if working: self.working_count += 1 - if retry: self.retry_count += 1 - if self.current % 10 == 0 or self.current == self.total: - elapsed = time.time() - self.start_time - speed = self.current / elapsed if elapsed > 0 else 0 - print(f"\r📊 [V.O.I.D][{self.current}/{self.total}] ✅:{self.working_count} 🔄:{self.retry_count} {speed:.1f} к/с {status}", end='', flush=True) - - def finish(self): - elapsed = time.time() - self.start_time - print(f"\r✅ Готово! {self.current} конфигов за {elapsed:.1f}с ({self.current/elapsed:.1f} к/с), рабочих: {self.working_count}") - -class PortManager: - def __init__(self, start=20000, end=25000): - self.ports = list(range(start, end + 1)) - self.used = set() - self.lock = threading.Lock() - - def get_port(self): - with self.lock: - available = [p for p in self.ports if p not in self.used] - if not available: return None - port = random.choice(available) - self.used.add(port) - return port - - def release_port(self, port): - with self.lock: - self.used.discard(port) - -class XrayTester: - def __init__(self, input_file='url_encoded.txt', output_file='url_work.txt', max_workers=30): - self.input_file = input_file - self.output_file = output_file - self.max_workers = max_workers - self.test_url = XRAY_TEST_URL - self.timeout = XRAY_TIMEOUT - self.max_retries = MAX_RETRIES - self.retry_delay = RETRY_DELAY - self.xray_dir = Path('./xray_bin') - self.xray_path = self.xray_dir / ('xray.exe' if os.name == 'nt' else 'xray') - self.port_manager = PortManager() - self.debug_file = DEBUG_FILE - self.xray_log_file = XRAY_LOG_FILE - self._ensure_xray() - - def _ensure_xray(self): - if not self.xray_path.exists(): - print("⬇️ Скачиваю Xray...") - self._download_xray() - else: - try: - result = subprocess.run([str(self.xray_path), '-version'], capture_output=True, text=True, timeout=5) - print(f"✅ Xray ядро готово") - except: - self._download_xray() - - def _download_xray(self): - import urllib.request, zipfile, platform - self.xray_dir.mkdir(exist_ok=True) - system = platform.system().lower() - if system == 'windows': - url = "https://github.com/XTLS/Xray-core/releases/latest/download/Xray-windows-64.zip" - else: - url = "https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip" - - zip_path = self.xray_dir / "xray.zip" - try: - urllib.request.urlretrieve(url, zip_path) - with zipfile.ZipFile(zip_path, 'r') as zip_ref: - zip_ref.extractall(self.xray_dir) - zip_path.unlink() - if os.name != 'nt': os.chmod(self.xray_path, 0o755) - except Exception as e: - print(f"❌ Ошибка загрузки Xray: {e}") - - def parse_url(self, url): - try: - schemes = ("vless://", "trojan://", "hysteria2://", "hy2://") - scheme = next((s for s in schemes if url.startswith(s)), None) - if not scheme: return None - - content = url[len(scheme):] - at_pos = content.find('@') - if at_pos == -1: return None - - auth = content[:at_pos] - after_at = content[at_pos+1:] - - q_pos = after_at.find('?') - host_part = after_at[:q_pos] if q_pos != -1 else after_at - query = after_at[q_pos+1:] if q_pos != -1 else "" - - host_part = host_part.split('#')[0] - host, port = (host_part.split(':', 1) + [443])[:2] - - return { - 'scheme': scheme.replace("://", ""), - 'auth': auth, 'host': host, - 'port': int(port), - 'params': dict(urllib.parse.parse_qsl(query)), - 'url': url - } - except: return None - - def create_xray_config(self, parsed, port): - try: - p = parsed['params'] - protocol = parsed['scheme'] - if protocol == "hy2": protocol = "hysteria2" - - outbound = { - "protocol": protocol, - "settings": {}, - "streamSettings": { - "network": p.get('type', 'tcp'), - "security": p.get('security', 'none') - }, - "tag": "proxy" - } - - if protocol == "vless": - outbound["settings"] = {"vnext": [{"address": parsed['host'], "port": parsed['port'], "users": [{"id": parsed['auth'], "encryption": "none", "flow": p.get('flow', '')}]}]} - elif protocol == "trojan": - outbound["settings"] = {"servers": [{"address": parsed['host'], "port": parsed['port'], "password": parsed['auth']}]} - elif protocol == "hysteria2": - outbound["settings"] = {"servers": [{"address": parsed['host'], "port": parsed['port'], "password": parsed['auth']}]} - outbound["streamSettings"]["network"] = "udp" - - # Reality / TLS - if p.get('security') == 'reality': - outbound["streamSettings"]["realitySettings"] = { - "serverName": p.get('sni', parsed['host']), "fingerprint": p.get('fp', 'chrome'), - "publicKey": p.get('pbk', ''), "shortId": p.get('sid', ''), "spiderX": p.get('spx', '/') - } - elif p.get('security') == 'tls': - outbound["streamSettings"]["tlsSettings"] = {"serverName": p.get('sni', parsed['host']), "allowInsecure": True} - - config = { - "log": {"loglevel": "error"}, - "inbounds": [{"port": port, "protocol": "socks", "settings": {"auth": "noauth", "udp": False}}], - "outbounds": [outbound] - } - return config - except: return None - - def test_with_xray(self, parsed, port): - config_file, process = None, None - try: - config = self.create_xray_config(parsed, port) - if not config: return "FAIL" - - fd, config_file = tempfile.mkstemp(suffix='.json') - os.close(fd) - with open(config_file, 'w', encoding='utf-8') as f: - json.dump(config, f) - - flags = subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0 - process = subprocess.Popen([str(self.xray_path), '-c', config_file], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=flags) - - time.sleep(1.2) - if process.poll() is not None: return "CRASH" - - start = time.time() - try: - r = requests.get(self.test_url, proxies={'https': f'socks5://127.0.0.1:{port}'}, timeout=self.timeout) - if r.status_code in [200, 204]: - return {'working': True, 'ping': (time.time() - start) * 1000} - except: pass - return "FAIL" - finally: - if process: - try: - process.terminate() - process.wait(timeout=1) - except: pass - if config_file and os.path.exists(config_file): - try: os.remove(config_file) - except: pass - - def test_one(self, url): - parsed = self.parse_url(url) - if not parsed: return None - - port = self.port_manager.get_port() - if not port: return None - - try: - for _ in range(self.max_retries): - res = self.test_with_xray(parsed, port) - if isinstance(res, dict): return {'url': url, 'ping': res['ping']} - time.sleep(self.retry_delay) - finally: - self.port_manager.release_port(port) - return None - - def test_all(self): - if not os.path.exists(self.input_file): return - with open(self.input_file, 'r', encoding='utf-8') as f: - all_urls = [line.strip() for line in f if line.strip()] - - if not all_urls: return - print(f"\n🔍 Тестирование {len(all_urls)} конфигов (VLESS/Trojan/Hy2)") - - working = [] - progress = SimpleProgress(len(all_urls)) - - with ThreadPoolExecutor(max_workers=self.max_workers) as executor: - futures = {executor.submit(self.test_one, url): url for url in all_urls} - for future in as_completed(futures): - res = future.result() - if res: - working.append(res) - progress.update('✅', working=True) - else: - progress.update('❌') - - progress.finish() - working.sort(key=lambda x: x['ping']) - - with open(self.output_file, 'w', encoding='utf-8') as f: - for w in working: f.write(w['url'] + '\n') - - print(f"📊 Результаты: ✅ Рабочих: {len(working)}\n") - - def run(self): - self.test_all() - -# ========= ОСНОВНОЙ ЦИКЛ ========= -async def main_cycle(): - global cycle_counter - cycle_counter += 1 - print(f"\n=== Новый цикл #{cycle_counter} [V.O.I.D Project] ===") - - if os.path.exists(OUTPUT_FILE): os.remove(OUTPUT_FILE) - if not os.path.exists(SOURCES_FILE): - print(f"❌ Файл {SOURCES_FILE} не найден!") - return - - try: - with open(SOURCES_FILE, "r", encoding="utf-8") as f: - urls = [line.strip() for line in f if line.strip()] - except: return - - if not urls: return - - sem = asyncio.Semaphore(THREADS_DOWNLOAD) - output_queue = asyncio.Queue() - stats = {"processed": 0, "found": 0} - stats_lock = asyncio.Lock() - - writer = asyncio.create_task(writer_task(output_queue, OUTPUT_FILE)) - - async with aiohttp.ClientSession() as session: - tasks = [process_url(session, url, sem, output_queue, stats_lock, stats) for url in urls] - await asyncio.gather(*tasks) - - await output_queue.put(None) - await writer - - if stats['found'] > 0: - await clean_configs() - await filter_configs() - await rename_configs() - await encode_all_configs() - - print("\n=== Запуск Xray-проверки ===") - tester = XrayTester(input_file=ENCODED_FILE, output_file=WORK_FILE, max_workers=XRAY_MAX_WORKERS) - await asyncio.get_event_loop().run_in_executor(None, tester.run) - else: - print("⏭️ Новых конфигов не обнаружено") - -async def run_forever(): - while True: - try: - await main_cycle() - print(f"📡 Ожидание следующего цикла ({CYCLE_DELAY}с)...") - await asyncio.sleep(CYCLE_DELAY) - except KeyboardInterrupt: break - except Exception as e: - print(f"⚠️ Ошибка: {e}") - await asyncio.sleep(60) - -if __name__ == "__main__": - try: - # Для первого запуска выполняем один цикл, либо run_forever() для сервера - asyncio.run(main_cycle()) - except KeyboardInterrupt: - print("\n👋 Парсер остановлен пользователем")