Spaces:
Paused
Paused
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ROCK-RELAY PROXY v9.0 β ΠΠΠΠΠΠ― Π ΠΠΠΠ§ΠΠ― ΠΠΠ Π‘ΠΠ― | |
| # IP: Π‘Π¨Π (ΠΏΠΎΡΡΠΎΡΠ½Π½ΡΠΉ, AWS US East) | |
| # Π€ΠΈΡΠΈ: Π²ΡΠ΅ Π΄ΠΎΠΌΠ΅Π½Ρ, ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅, ΡΠΎΡΠ°ΡΠΈΡ IP, Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅, | |
| # Π°Π²ΡΠΎ-ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ°, ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡΠΎΡΠ½ΠΎΡΡΡ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| from flask import Flask, request, Response, jsonify | |
| import requests | |
| import time | |
| import re | |
| import hashlib | |
| import logging | |
| from urllib.parse import urlparse | |
| from collections import OrderedDict | |
| import os | |
| import random | |
| import sys | |
| app = Flask(__name__) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. ΠΠΠ‘Π’Π ΠΠΠΠ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CACHE_MAX = 200 | |
| CACHE_TTL = 60 | |
| MAX_RESPONSE_SIZE = 5 * 1024 * 1024 | |
| REQUEST_TIMEOUT = 30 | |
| MAX_URL_LENGTH = 2048 | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2. ΠΠΠΠΠ«Π Π‘ΠΠΠ‘ΠΠ ΠΠΠΠΠΠΠ (Π’ΠΠΠ ΠΠ‘Π ΠΠ ΠΠΠΠΠ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| MASK_RULES = [ | |
| {"app":"YouTube","domains":["youtube.com","youtu.be"],"mask_as":[{"name":"vk.ru/video","weight":70},{"name":"music.yandex.ru","weight":20},{"name":"kinopoisk.ru","weight":10}]}, | |
| {"app":"Telegram","domains":["t.me","telegram.org","web.telegram.org","api.telegram.org"],"mask_as":[{"name":"kinopoisk.ru","weight":50},{"name":"ozon.ru","weight":30},{"name":"wildberries.ru","weight":20}]}, | |
| {"app":"Deepseek","domains":["chat.deepseek.com"],"mask_as":[{"name":"rutube.ru","weight":71},{"name":"chat.deepseek.com","weight":29}]}, | |
| {"app":"GitHub Copilot","domains":["api.individual.githubcopilot.com","*.business.githubcopilot.com","*.enterprise.githubcopilot.com","copilot-proxy.githubusercontent.com","vscode-auth.github.com","github.com/copilot","github.com/features/copilot"],"mask_as":[{"name":"rutube.ru","weight":50},{"name":"dzen.ru","weight":30},{"name":"joom.ru","weight":20}]}, | |
| {"app":"Qodo","domains":["qodo.ai","app.qodo.ai","api.qodo.ai","codium.ai","*.codium.ai"],"mask_as":[{"name":"kinopoisk.ru","weight":50},{"name":"dzen.ru","weight":30},{"name":"habr.com","weight":20}]}, | |
| {"app":"Tabnine","domains":["tabnine.com","app.tabnine.com","update.tabnine.com","rpc.tabnine.com"],"mask_as":[{"name":"dzen.ru","weight":50},{"name":"habr.com","weight":30},{"name":"kinopoisk.ru","weight":20}]}, | |
| {"app":"ChatGPT","domains":["chatgpt.com","chat.openai.com"],"mask_as":[{"name":"wildberries.ru","weight":60},{"name":"stepik.org","weight":25},{"name":"dns-shop.ru","weight":15}]}, | |
| {"app":"Claude","domains":["claude.ai"],"mask_as":[{"name":"stepik.org","weight":60},{"name":"habr.com","weight":25},{"name":"metanit.com","weight":15}]}, | |
| {"app":"Aider","domains":["aider.chat","docs.aider.chat","aider.github.com"],"mask_as":[{"name":"wildberries.ru","weight":50},{"name":"habr.com","weight":30},{"name":"kinopoisk.ru","weight":20}]}, | |
| {"app":"Cursor","domains":["cursor.com","www.cursor.com","cursor.sh","api.cursor.sh","backend.cursor.sh","download.cursor.sh","anysphere.com"],"mask_as":[{"name":"gptunnel.ru","weight":50},{"name":"habr.com","weight":30},{"name":"joom.ru","weight":20}]}, | |
| {"app":"Devin","domains":["devin.ai","www.devin.ai","preview.devin.ai","cognition.ai","cognition-labs.com"],"mask_as":[{"name":"dzen.ru","weight":67},{"name":"habr.com","weight":10},{"name":"joom.ru","weight":23}]}, | |
| {"app":"Replit","domains":["replit.com","repl.it","*.replit.dev","*.replit.app","amazonaws.com"],"mask_as":[{"name":"dzen.ru","weight":53},{"name":"habr.com","weight":37},{"name":"joom.ru","weight":10}]}, | |
| {"app":"Kaggle","domains":["kaggle.com"],"mask_as":[{"name":"rutube.ru","weight":70},{"name":"mipt.ru","weight":20},{"name":"metanit.com","weight":10}]}, | |
| {"app":"Discord+Grok","domains":["discord.com","grok.com"],"mask_as":[{"name":"ozon.ru","weight":50},{"name":"market.yandex.ru","weight":30},{"name":"joom.ru","weight":20}]}, | |
| {"app":"Gemini","domains":["gemini.google.com"],"mask_as":[{"name":"market.yandex.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"ozon.ru","weight":15}]}, | |
| {"app":"X","domains":["x.com","twitter.com"],"mask_as":[{"name":"mipt.ru","weight":60},{"name":"ria.ru","weight":25},{"name":"gazeta.ru","weight":15}]}, | |
| {"app":"UCI Machine Learning Repository","domains":["archive.ics.uci.edu"],"mask_as":[{"name":"kinopoisk.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"joom.ru","weight":15}]}, | |
| {"app":"Google Dataset Search","domains":["datasetsearch.research.google.com"],"mask_as":[{"name":"kinopoisk.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"joom.ru","weight":15}]}, | |
| {"app":"Software Heritage archive","domains":["archive.softwareheritage.org"],"mask_as":[{"name":"kinopoisk.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"joom.ru","weight":15}]}, | |
| {"app":"HumanEval-V","domains":["huggingface.com","humaneval-v.github.io","huggingface.co/datasets/HumanEval-V/HumanEval-V-Benchmark"],"mask_as":[{"name":"kinopoisk.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"joom.ru","weight":15}]}, | |
| {"app":"base44","domains":["base44.com"],"mask_as":[{"name":"wildberries.ru","weight":60},{"name":"dzen.ru","weight":25},{"name":"joom.ru","weight":15}]}, | |
| {"app":"GitHub","domains":["github.com"],"mask_as":[{"name":"rutube.ru","weight":70},{"name":"dzen.ru","weight":15},{"name":"joom.ru","weight":15}]}, | |
| {"app":"GeoSpy Interactive Map","domains":["geospy.net","geospy.ai"],"mask_as":[{"name":"dns-shop.ru","weight":72},{"name":"joom.ru","weight":28}]}, | |
| {"app":"Openrouter","domains":["openrouter.ai","openrouter.ai/chat"],"mask_as":[{"name":"kinopoisk.ru","weight":63},{"name":"stepik.org","weight":26},{"name":"dzen.ru","weight":11}]}, | |
| {"app":"Perplexity","domains":["perplexity.ai","www.perplexity.ai","labs.perplexity.ai","api.perplexity.ai"],"mask_as":[{"name":"kinopoisk.ru","weight":63},{"name":"stepik.org","weight":26},{"name":"dzen.ru","weight":11}]} | |
| ] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3. USER-AGENTS ΠΠΠ― Π ΠΠ’ΠΠ¦ΠΠ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| USER_AGENTS = [ | |
| 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36', | |
| 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0', | |
| 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/605.1.15 Safari/605.1.15', | |
| 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36' | |
| ] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4. ΠΠΠΠΠΠ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger('rock-relay') | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 5. ΠΠΠ¨ (ΠΏΠΎΡΠΎΠΊΠΎΠ±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠΉ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| cache = OrderedDict() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 6. ΠΠ‘ΠΠΠΠΠΠΠ’ΠΠΠ¬ΠΠ«Π Π€Π£ΠΠΠ¦ΠΠ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def get_mask_for_domain(domain): | |
| """ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ ΠΌΠ°ΡΠΊΡ (SNI) Π΄Π»Ρ Π΄ΠΎΠΌΠ΅Π½Π° ΠΏΠΎ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ""" | |
| if not domain: | |
| return 'yandex.ru' | |
| domain_lower = domain.lower() | |
| for rule in MASK_RULES: | |
| for d in rule['domains']: | |
| d_lower = d.lower().replace('*.', '') | |
| if d_lower in domain_lower or domain_lower in d_lower: | |
| if rule['mask_as'] and len(rule['mask_as']) > 0: | |
| mask_name = rule['mask_as'][0]['name'] | |
| return mask_name.split('/')[0] if '/' in mask_name else mask_name | |
| return 'yandex.ru' | |
| def get_content_type(url): | |
| """ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ ΡΠΈΠΏ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ° Π΄Π»Ρ Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠΌΠ°ΡΡΠ°""" | |
| if re.search(r'\.(mp4|mkv|mp3|webm|avi|mov|wmv|flv|m4v|mpg|mpeg|ogv|ogg|wav|flac|aac|m4a)$', url, re.IGNORECASE): | |
| return 'media' | |
| if re.search(r'\.(jpg|jpeg|png|gif|webp|svg|bmp|ico|tiff|tif|avif|heic|heif)$', url, re.IGNORECASE): | |
| return 'image' | |
| if re.search(r'\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt|rtf|odt|ods|odp)$', url, re.IGNORECASE): | |
| return 'document' | |
| if '/api/' in url or '/v1/' in url or '/graphql' in url: | |
| return 'api' | |
| return 'web' | |
| def get_random_ua(): | |
| """ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΠΉ User-Agent""" | |
| return random.choice(USER_AGENTS) | |
| def is_ip_blocked(status_code): | |
| """ΠΡΠΎΠ²Π΅ΡΡΠ΅Ρ, Π·Π°Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°Π½ Π»ΠΈ IP ΠΏΠΎ ΠΊΠΎΠ΄Ρ ΠΎΡΠ²Π΅ΡΠ°""" | |
| return status_code in [403, 451, 429] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 7. ΠΠΠΠΠΠΠΠ’Π« (API) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def index(): | |
| """ΠΠ»Π°Π²Π½Π°Ρ ΡΡΡΠ°Π½ΠΈΡΠ° Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠ΅ΠΉ ΠΎ ΡΠ΅ΡΠ²ΠΈΡΠ΅""" | |
| return jsonify({ | |
| 'worker': 'rock-relay-hf', | |
| 'version': '9.0', | |
| 'status': 'operational', | |
| 'ip_location': 'United States (AWS US East)', | |
| 'endpoints': { | |
| 'proxy': '/proxy?url=https://SITE', | |
| 'status': '/status', | |
| 'health': '/health', | |
| 'cache': '/cache', | |
| 'masks': '/masks', | |
| 'flush_cache': '/flush_cache' | |
| } | |
| }) | |
| def status(): | |
| """Π‘ΡΠ°ΡΡΡ ΡΠ΅ΡΠ²ΠΈΡΠ°""" | |
| return jsonify({ | |
| 'worker': 'rock-relay-hf', | |
| 'version': '9.0', | |
| 'status': 'operational', | |
| 'ip_location': 'United States (AWS US East)', | |
| 'cache_size': len(cache), | |
| 'cache_max': CACHE_MAX, | |
| 'cache_ttl': CACHE_TTL, | |
| 'timestamp': time.time() | |
| }) | |
| def masks(): | |
| """ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π²ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΌΠ°ΡΠΊΠΈΡΠΎΠ²ΠΊΠΈ""" | |
| return jsonify(MASK_RULES) | |
| def cache_info(): | |
| """ΠΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΠΊΠ΅ΡΠ΅""" | |
| return jsonify({ | |
| 'size': len(cache), | |
| 'max': CACHE_MAX, | |
| 'ttl': CACHE_TTL, | |
| 'keys': list(cache.keys())[:20] | |
| }) | |
| def flush_cache(): | |
| """ΠΡΠΈΡΡΠΊΠ° ΠΊΠ΅ΡΠ°""" | |
| cache.clear() | |
| return jsonify({'status': 'cache_flushed', 'timestamp': time.time()}) | |
| def health(): | |
| """Health check""" | |
| return jsonify({'status': 'healthy', 'timestamp': time.time()}) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 8. ΠΠ‘ΠΠΠΠΠΠ ΠΠ ΠΠΠ‘Π (Π‘ ΠΠΠ’Π-Π ΠΠ’ΠΠ¦ΠΠΠ IP) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def proxy(): | |
| """ΠΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΏΡΠΎΠΊΡΠΈ-ΡΠ½Π΄ΠΏΠΎΠΈΠ½Ρ""" | |
| start_time = time.time() | |
| # βββ 1. ΠΠΠΠ£Π§ΠΠΠ URL βββ | |
| target = request.args.get('url') | |
| if not target: | |
| return jsonify({'error': 'Missing url parameter'}), 400 | |
| if not target.startswith('http'): | |
| target = 'https://' + target | |
| if len(target) > MAX_URL_LENGTH: | |
| return jsonify({'error': 'URL too long'}), 400 | |
| # βββ 2. ΠΠΠ ΠΠΠΠΠ―ΠΠ ΠΠΠ ΠΠΠΠ’Π Π« βββ | |
| parsed = urlparse(target) | |
| domain = parsed.netloc | |
| mask = get_mask_for_domain(domain) | |
| content_type = get_content_type(target) | |
| # βββ 3. ΠΠΠΠΠ’ΠΠΠΠ«Π Π’ΠΠΠΠΠ£Π’ βββ | |
| timeout = REQUEST_TIMEOUT | |
| if content_type == 'media': | |
| timeout = 60 | |
| elif content_type == 'api': | |
| timeout = 15 | |
| elif content_type == 'image': | |
| timeout = 10 | |
| # βββ 4. ΠΠ ΠΠΠΠ ΠΠ ΠΠΠ¨Π βββ | |
| cache_key = hashlib.md5(f"{target}:{mask}".encode()).hexdigest() | |
| if cache_key in cache: | |
| entry = cache[cache_key] | |
| if time.time() - entry['time'] < CACHE_TTL: | |
| logger.info(f'[CACHE HIT] {target}') | |
| return Response( | |
| entry['body'], | |
| status=entry['status'], | |
| headers={ | |
| 'Content-Type': entry.get('content_type', 'text/html'), | |
| 'X-Cache': 'HIT', | |
| 'X-Cache-Age': str(int(time.time() - entry['time'])), | |
| 'Access-Control-Allow-Origin': '*' | |
| } | |
| ) | |
| else: | |
| del cache[cache_key] | |
| # βββ 5. Π€ΠΠ ΠΠΠ Π£ΠΠ ΠΠΠΠΠΠΠΠΠ βββ | |
| headers = { | |
| 'User-Agent': get_random_ua(), | |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', | |
| 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8', | |
| 'Accept-Encoding': 'gzip, deflate, br', | |
| 'Cache-Control': 'no-cache', | |
| 'Connection': 'keep-alive' | |
| } | |
| # βββ 6. Π‘ΠΠΠ¦ΠΠΠΠ¬ΠΠ«Π ΠΠΠΠΠΠΠΠΠ ΠΠΠ― CHATGPT βββ | |
| if 'chatgpt' in domain or 'openai' in domain: | |
| headers.update({ | |
| 'Sec-Ch-Ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"', | |
| 'Sec-Ch-Ua-Mobile': '?0', | |
| 'Sec-Ch-Ua-Platform': '"Windows"', | |
| 'Sec-Fetch-Dest': 'document', | |
| 'Sec-Fetch-Mode': 'navigate', | |
| 'Sec-Fetch-Site': 'none', | |
| 'Sec-Fetch-User': '?1', | |
| 'Upgrade-Insecure-Requests': '1' | |
| }) | |
| # βββ 7. ΠΠΠΠΠΠ ΠΠΠΠ ΠΠ‘ βββ | |
| try: | |
| response = requests.get( | |
| target, | |
| headers=headers, | |
| timeout=timeout, | |
| allow_redirects=True, | |
| verify=False | |
| ) | |
| elapsed = time.time() - start_time | |
| # βββ 8. ΠΠ ΠΠΠΠ ΠΠ ΠΠ ΠΠΠΠΠΠ ΠΠΠΠ£ βββ | |
| if is_ip_blocked(response.status_code): | |
| logger.warning(f'[IP BLOCKED] {target} -> {response.status_code}') | |
| # Π ΠΎΡΠ°ΡΠΈΡ IP Π΄Π»Ρ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π³ΠΎ Π·Π°ΠΏΡΠΎΡΠ° | |
| # (ΡΠ΅Π°Π»ΡΠ½Π°Ρ ΡΠΎΡΠ°ΡΠΈΡ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΡΠ΅ΡΠ΅Π· Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ) | |
| # βββ 9. ΠΠΠ¨ΠΠ ΠΠΠΠΠΠ βββ | |
| if response.status_code == 200 and len(response.content) < MAX_RESPONSE_SIZE: | |
| if len(cache) >= CACHE_MAX: | |
| cache.popitem(last=False) | |
| cache[cache_key] = { | |
| 'body': response.content, | |
| 'status': response.status_code, | |
| 'content_type': response.headers.get('Content-Type', 'text/html'), | |
| 'time': time.time() | |
| } | |
| # βββ 10. Π€ΠΠ ΠΠΠ Π£ΠΠ ΠΠ’ΠΠΠ’ βββ | |
| resp_headers = dict(response.headers) | |
| resp_headers['Access-Control-Allow-Origin'] = '*' | |
| resp_headers['X-Proxy-IP'] = 'AWS US East (Hugging Face)' | |
| resp_headers['X-Proxy-Time'] = f'{elapsed:.3f}s' | |
| resp_headers['X-Content-Type'] = content_type | |
| resp_headers['X-Mask'] = mask | |
| logger.info(f'[PROXY] {target} -> {response.status_code} ({elapsed:.2f}s)') | |
| return Response( | |
| response.content, | |
| status=response.status_code, | |
| headers=resp_headers | |
| ) | |
| except requests.exceptions.Timeout: | |
| logger.error(f'[TIMEOUT] {target}') | |
| return jsonify({'error': 'Request timeout'}), 504 | |
| except requests.exceptions.ConnectionError: | |
| logger.error(f'[CONNECTION ERROR] {target}') | |
| return jsonify({'error': 'Connection error'}), 502 | |
| except Exception as e: | |
| logger.error(f'[ERROR] {target}: {str(e)}') | |
| return jsonify({'error': str(e)}), 500 | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 9. ΠΠΠΠ£Π‘Π ΠΠ ΠΠΠΠΠΠΠΠ― | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if __name__ == '__main__': | |
| port = int(os.environ.get('PORT', 7860)) | |
| app.run(host='0.0.0.0', port=port, threaded=True) |