Merge upstream/main into android_migration

This commit is contained in:
Dark_Avery
2026-03-19 20:26:56 +03:00
13 changed files with 2358 additions and 128 deletions

View File

@@ -1 +1 @@
"""TG WS Proxy core package."""
__version__ = "1.1.3"

View File

@@ -19,8 +19,8 @@ DEFAULT_PORT = 1080
log = logging.getLogger('tg-ws-proxy')
_TCP_NODELAY = True
_RECV_BUF = 65536
_SEND_BUF = 65536
_RECV_BUF = 256 * 1024
_SEND_BUF = 256 * 1024
_WS_POOL_SIZE = 4
_WS_POOL_MAX_AGE = 120.0
@@ -65,6 +65,13 @@ _IP_TO_DC: Dict[str, Tuple[int, bool]] = {
'149.154.171.5': (5, False),
'91.108.56.102': (5, True), '91.108.56.128': (5, True),
'91.108.56.151': (5, True),
# DC203
'91.105.192.100': (203, False),
}
# This case might work but not actually sure
_DC_OVERRIDES: Dict[int, int] = {
203: 2
}
_dc_opt: Dict[int, Optional[str]] = {}
@@ -76,7 +83,8 @@ _ws_blacklist: Set[Tuple[int, bool]] = set()
# Rate-limit re-attempts per (dc, is_media)
_dc_fail_until: Dict[Tuple[int, bool], float] = {}
_DC_FAIL_COOLDOWN = 60.0 # seconds
_DC_FAIL_COOLDOWN = 30.0 # seconds to keep reduced WS timeout after failure
_WS_FAIL_TIMEOUT = 2.0 # quick-retry timeout after a recent WS failure
_ssl_ctx = ssl.create_default_context()
@@ -375,7 +383,7 @@ def _dc_from_init(data: bytes) -> Tuple[Optional[int], bool]:
proto, dc_raw, plain.hex())
if proto in (0xEFEFEFEF, 0xEEEEEEEE, 0xDDDDDDDD):
dc = abs(dc_raw)
if 1 <= dc <= 5:
if 1 <= dc <= 5 or dc == 203:
return dc, (dc_raw < 0)
except Exception as exc:
log.debug("DC extraction failed: %s", exc)
@@ -460,6 +468,7 @@ class _MsgSplitter:
def _ws_domains(dc: int, is_media) -> List[str]:
dc = _DC_OVERRIDES.get(dc, dc)
if is_media is None or is_media:
return [f'kws{dc}-1.web.telegram.org', f'kws{dc}.web.telegram.org']
return [f'kws{dc}.web.telegram.org', f'kws{dc}-1.web.telegram.org']
@@ -915,20 +924,10 @@ async def _handle_client(reader, writer):
label, dc, media_tag)
return
# -- Cooldown check --
fail_until = _dc_fail_until.get(dc_key, 0)
if now < fail_until:
remaining = fail_until - now
log.debug("[%s] DC%d%s WS cooldown (%.0fs) -> TCP",
label, dc, media_tag, remaining)
ok = await _tcp_fallback(reader, writer, dst, port, init,
label, dc=dc, is_media=is_media)
if ok:
log.info("[%s] DC%d%s TCP fallback closed",
label, dc, media_tag)
return
# -- Try WebSocket via direct connection --
fail_until = _dc_fail_until.get(dc_key, 0)
ws_timeout = _WS_FAIL_TIMEOUT if now < fail_until else 10.0
domains = _ws_domains(dc, is_media)
target = _dc_opt[dc]
ws = None
@@ -946,7 +945,7 @@ async def _handle_client(reader, writer):
label, dc, media_tag, dst, port, url, target)
try:
ws = await RawWebSocket.connect(target, domain,
timeout=10)
timeout=ws_timeout)
all_redirects = False
break
except WsHandshakeError as exc:
@@ -1131,13 +1130,16 @@ def main():
ap.add_argument('--host', type=str, default='127.0.0.1',
help='Listen host (default 127.0.0.1)')
ap.add_argument('--dc-ip', metavar='DC:IP', action='append',
default=['2:149.154.167.220', '4:149.154.167.220'],
default=[],
help='Target IP for a DC, e.g. --dc-ip 1:149.154.175.205'
' --dc-ip 2:149.154.167.220')
ap.add_argument('-v', '--verbose', action='store_true',
help='Debug logging')
args = ap.parse_args()
if not args.dc_ip:
args.dc_ip = ['2:149.154.167.220', '4:149.154.167.220']
try:
dc_opt = parse_dc_ip_list(args.dc_ip)
except ValueError as e: