From ba89cad8b8dbba50407731d16b79794fc973e880 Mon Sep 17 00:00:00 2001 From: Flowseal Date: Sat, 11 Apr 2026 20:52:24 +0300 Subject: [PATCH] fake-tls cli --- docs/README.md | 97 +++++++++++++--- proxy/config.py | 2 + proxy/fake_tls.py | 256 +++++++++++++++++++++++++++++++++++++++++++ proxy/stats.py | 2 + proxy/tg_ws_proxy.py | 136 +++++++++++++++++++++-- utils/tray_common.py | 1 + 6 files changed, 471 insertions(+), 23 deletions(-) create mode 100644 proxy/fake_tls.py diff --git a/docs/README.md b/docs/README.md index 54d5c53..0c66825 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,7 +5,7 @@ > **USDT (TRC20)**: `TXPnKs2Ww1RD8JN6nChFUVmi5r2hqrWjuu` > **BTC**: `bc1qr8vd6jelkyyry3m4mq6z5txdx4pl856fu6ss0w` > **ETH**: `0x1417878fdc5047E670a77748B34819b9A49C72F1` -> Другие монеты: https://nowpayments.io/donation/flowseal +> **Другие монеты**: https://nowpayments.io/donation/flowseal > [!CAUTION] > @@ -39,7 +39,9 @@ Telegram Desktop → MTProto Proxy (127.0.0.1:1443) → WebSocket → Telegram D > [!IMPORTANT] > ### Не грузит фото/видео? -> ### Удалите в настройках прокси в DC->IP всё, кроме `4:149.154.167.220` +> **Удалите в настройках прокси в DC->IP всё, кроме `4:149.154.167.220`** +> **Если не помогло, то удалите вообще всё из этого поля** +> #### > Подобная проблема встречается на аккаунтах без Premium > Если вам не помогло, то настраивайте свой домен по гайду отсюда: https://github.com/Flowseal/tg-ws-proxy/blob/main/docs/CfProxy.md @@ -54,6 +56,7 @@ Telegram Desktop → MTProto Proxy (127.0.0.1:1443) → WebSocket → Telegram D **Меню трея:** - **Открыть в Telegram** — автоматически настроить прокси через `tg://proxy` ссылку +- **Скопировать ссылку** — скопировать ссылку для подключения - **Перезапустить прокси** — перезапуск без выхода из приложения - **Настройки...** — GUI-редактор конфигурации (в т.ч. версия приложения, опциональная проверка обновлений с GitHub) - **Открыть логи** — открыть файл логов @@ -61,6 +64,26 @@ Telegram Desktop → MTProto Proxy (127.0.0.1:1443) → WebSocket → Telegram D При первом запуске после старта может появиться запрос об открытии страницы релиза, если на GitHub вышла новая версия (отключается в настройках). +### Настройка Telegram Desktop + +### Автоматически: + +ПКМ по иконке в трее → **«Открыть в Telegram»** +Если не сработало (не открылся Telegram с подключением), то: +1. ПКМ по иконке в трее → **«Скопировать ссылку»** +2. Отправьте ссылку себе в избранное в Telegram клиенте и нажмите по ней ЛКМ +3. Подключитесь + +### Вручную: + +1. Telegram → **Настройки** → **Продвинутые настройки** → **Тип подключения** → **Прокси** +2. Добавить прокси: + - **Тип:** MTProto + - **Сервер:** `127.0.0.1` (или переопределенный вами) + - **Порт:** `1443` (или переопределенный вами) + - **Secret:** из настроек или логов + +## ### macOS Перейдите на [страницу релизов](https://github.com/Flowseal/tg-ws-proxy/releases) и скачайте **`TgWsProxy_macos_universal.dmg`** — универсальная сборка для Apple Silicon и Intel. @@ -147,6 +170,8 @@ tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v] | `--no-cfproxy` | `false` | Отключить попытку [проксирования через Cloudflare]((https://github.com/Flowseal/tg-ws-proxy/blob/main/docs/CfProxy.md)) | | `--cfproxy-domain` | | Указать свой домен для проксирования через Cloudfalre. [Подробнее тут](https://github.com/Flowseal/tg-ws-proxy/blob/main/docs/CfProxy.md) | | `--cfproxy-priority` | `true` | Пробовать проксировать через Cloudflare перед прямым TCP подключением | +| `--fake-tls-domain` | | Включить Fake TLS (ee-secret) маскировку с указанным SNI-доменом | +| `--proxy-protocol` | выкл. | Принимать HAProxy PROXY protocol v1 (для работы за nginx/haproxy с `proxy_protocol on`) | | `--buf-kb` | `256` | Размер буфера в КБ | | `--pool-size` | `4` | Количество заготовленных соединений на каждый DC | | `--log-file` | выкл. | Путь до файла, в который сохранять логи | @@ -165,24 +190,64 @@ tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220 # С подробным логированием tg-ws-proxy -v + +# Fake TLS маскировка (ee-secret) +tg-ws-proxy --fake-tls-domain example.com ``` -## Настройка Telegram Desktop +## Fake TLS + nginx upstream +### Домен (`--fake-tls-domain`) должен указывать на тот же IP, на котором стоит прокси -### Автоматически +**Пример `nginx.conf` (stream):** -ПКМ по иконке в трее → **«Открыть в Telegram»** +```nginx +upstream mtproto { + server 127.0.0.1:8446; +} -### Вручную +map $ssl_preread_server_name $sni_name { + hostnames; + example.com mtproto; + # if you have xray with selfsni running: + # sub.example.com www; + # default xray; +} -1. Telegram → **Настройки** → **Продвинутые настройки** → **Тип подключения** → **Прокси** -2. Добавить прокси: - - **Тип:** MTProto - - **Сервер:** `127.0.0.1` (или переопределенный вами) - - **Порт:** `1443` (или переопределенный вами) - - **Secret:** из настроек или логов +# upstream xray { +# server 127.0.0.1:8443; +# } +# +# upstream www { +# server 127.0.0.1:7443; +# } -## Конфигурация +server { + proxy_protocol on; + set_real_ip_from unix:; + listen 443; + proxy_pass $sni_name; + ssl_preread on; +} +``` + +**Запуск прокси за nginx:** + +```bash +python3 proxy/tg_ws_proxy.py \ + --port 8446 \ + --host 127.0.0.1 \ + --fake-tls-domain example.com \ + --proxy-protocol \ + --secret <32-hex-chars> +``` + +Ссылка для подключения будет в формате `ee`-секрета:

+ +``` +tg://proxy?server=your.domain.com&port=443&secret=ee +``` + +## Файлы конфигурации Tray-приложения Tray-приложение хранит данные в: @@ -203,7 +268,11 @@ Tray-приложение хранит данные в: "buf_kb": 256, "pool_size": 4, "log_max_mb": 5.0, - "check_updates": true + "check_updates": true, + "cfproxy": true, + "cfproxy_priority": true, + "cfproxy_user_domain": "", + "appearance": "auto" } ``` diff --git a/proxy/config.py b/proxy/config.py index c5f74e8..e9a1bd0 100644 --- a/proxy/config.py +++ b/proxy/config.py @@ -47,6 +47,8 @@ class ProxyConfig: cfproxy_user_domain: str = '' cfproxy_domains: List[str] = field(default_factory=lambda: list(CFPROXY_DEFAULT_DOMAINS)) active_cfproxy_domain: str = field(default_factory=lambda: random.choice(CFPROXY_DEFAULT_DOMAINS)) + fake_tls_domain: str = '' + proxy_protocol: bool = False proxy_config = ProxyConfig() diff --git a/proxy/fake_tls.py b/proxy/fake_tls.py new file mode 100644 index 0000000..9b7e0b2 --- /dev/null +++ b/proxy/fake_tls.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +import asyncio +import hmac +import hashlib +import os +import random +import struct +import time +import logging + +from typing import Optional, Tuple +from .stats import stats + + +log = logging.getLogger('tg-mtproto-proxy') + +TLS_RECORD_HANDSHAKE = 0x16 +TLS_RECORD_CCS = 0x14 +TLS_RECORD_APPDATA = 0x17 + +TLS_VERSION_10 = b'\x03\x01' +TLS_VERSION_12 = b'\x03\x03' +TLS_VERSION_13 = b'\x03\x04' + +CLIENT_RANDOM_OFFSET = 11 +CLIENT_RANDOM_LEN = 32 +SESSION_ID_OFFSET = 44 +SESSION_ID_LEN = 32 + +TIMESTAMP_TOLERANCE = 120 + +TLS_APPDATA_MAX = 16384 + + +_CCS_FRAME = b'\x14\x03\x03\x00\x01\x01' + +_SERVER_HELLO_TEMPLATE = bytearray( + b'\x16\x03\x03\x00\x7a' + b'\x02\x00\x00\x76' + b'\x03\x03' + + b'\x00' * 32 + + b'\x20' + + b'\x00' * 32 + + b'\x13\x01\x00' + + b'\x00\x2e' + + b'\x00\x33\x00\x24\x00\x1d\x00\x20' + + b'\x00' * 32 + + b'\x00\x2b\x00\x02\x03\x04' +) + +_SH_RANDOM_OFF = 11 +_SH_SESSID_OFF = 44 +_SH_PUBKEY_OFF = 89 + + +def verify_client_hello(data: bytes, secret: bytes) -> Optional[Tuple[bytes, bytes, int]]: + n = len(data) + # 5 (record hdr) + 6 (hs type+len+version) + 32 (random) = 43 + if n < 43: + return None + if data[0] != TLS_RECORD_HANDSHAKE: + return None + if data[5] != 0x01: + return None + + client_random = bytes(data[CLIENT_RANDOM_OFFSET:CLIENT_RANDOM_OFFSET + CLIENT_RANDOM_LEN]) + + zeroed = bytearray(data) + zeroed[CLIENT_RANDOM_OFFSET:CLIENT_RANDOM_OFFSET + CLIENT_RANDOM_LEN] = b'\x00' * CLIENT_RANDOM_LEN + + expected = hmac.new(secret, bytes(zeroed), hashlib.sha256).digest() + + if not hmac.compare_digest(expected[:28], client_random[:28]): + return None + + ts_xor = bytes(client_random[28 + i] ^ expected[28 + i] for i in range(4)) + timestamp = struct.unpack(' TIMESTAMP_TOLERANCE: + return None + + session_id = b'\x00' * SESSION_ID_LEN + if n >= SESSION_ID_OFFSET + SESSION_ID_LEN and data[43] == 0x20: + session_id = bytes(data[SESSION_ID_OFFSET:SESSION_ID_OFFSET + SESSION_ID_LEN]) + + return client_random, session_id, timestamp + + +def build_server_hello(secret: bytes, client_random: bytes, session_id: bytes) -> bytes: + sh = bytearray(_SERVER_HELLO_TEMPLATE) + sh[_SH_SESSID_OFF:_SH_SESSID_OFF + 32] = session_id + sh[_SH_PUBKEY_OFF:_SH_PUBKEY_OFF + 32] = os.urandom(32) + + ccs = _CCS_FRAME + encrypted_size = random.randint(1900, 2100) + encrypted_data = os.urandom(encrypted_size) + app_record = b'\x17\x03\x03' + struct.pack('>H', encrypted_size) + encrypted_data + + response = bytes(sh) + ccs + app_record + + hmac_input = client_random + response + server_random = hmac.new(secret, hmac_input, hashlib.sha256).digest() + + final = bytearray(response) + final[_SH_RANDOM_OFF:_SH_RANDOM_OFF + 32] = server_random + + return bytes(final) + + +def wrap_tls_record(data: bytes) -> bytes: + parts = [] + offset = 0 + while offset < len(data): + chunk = data[offset:offset + TLS_APPDATA_MAX] + parts.append( + b'\x17\x03\x03' + + struct.pack('>H', len(chunk)) + + chunk + ) + offset += len(chunk) + return b''.join(parts) + + +class FakeTlsStream: + __slots__ = ('_reader', '_writer', '_read_buf', '_read_left') + + def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): + self._reader = reader + self._writer = writer + self._read_buf = bytearray() + self._read_left = 0 + + async def readexactly(self, n: int) -> bytes: + while len(self._read_buf) < n: + payload = await self._read_tls_payload() + if not payload: + raise asyncio.IncompleteReadError(bytes(self._read_buf), n) + self._read_buf.extend(payload) + result = bytes(self._read_buf[:n]) + del self._read_buf[:n] + return result + + async def read(self, n: int) -> bytes: + if self._read_buf: + chunk = bytes(self._read_buf[:n]) + del self._read_buf[:n] + return chunk + payload = await self._read_tls_payload() + if not payload: + return b'' + if len(payload) > n: + self._read_buf.extend(payload[n:]) + return payload[:n] + return payload + + async def _read_tls_payload(self) -> bytes: + if self._read_left > 0: + data = await self._reader.read(self._read_left) + if not data: + return b'' + self._read_left -= len(data) + return data + + while True: + hdr = await self._reader.readexactly(5) + rtype = hdr[0] + rec_len = struct.unpack('>H', hdr[3:5])[0] + + if rtype == TLS_RECORD_CCS: + if rec_len > 0: + await self._reader.readexactly(rec_len) + continue + + if rtype != TLS_RECORD_APPDATA: + return b'' + + data = await self._reader.read(min(rec_len, 65536)) + if not data: + return b'' + remaining = rec_len - len(data) + if remaining > 0: + self._read_left = remaining + return data + + def write(self, data: bytes) -> None: + self._writer.write(wrap_tls_record(data)) + + async def drain(self) -> None: + await self._writer.drain() + + def close(self) -> None: + self._writer.close() + + async def wait_closed(self) -> None: + await self._writer.wait_closed() + + def get_extra_info(self, name, default=None): + return self._writer.get_extra_info(name, default) + + @property + def transport(self): + return self._writer.transport + + def is_closing(self): + return self._writer.is_closing() + + +async def proxy_to_masking_domain(reader, writer, initial_data: bytes, + domain: str, label: str) -> None: + try: + up_reader, up_writer = await asyncio.wait_for( + asyncio.open_connection(domain, 443), timeout=10) + except Exception as exc: + log.debug("[%s] masking: cannot connect to %s:443: %s", + label, domain, exc) + return + + log.debug("[%s] masking -> %s:443", label, domain) + stats.connections_masked += 1 + + try: + if initial_data: + up_writer.write(initial_data) + await up_writer.drain() + + async def _relay(src, dst): + try: + while True: + chunk = await src.read(16384) + if not chunk: + break + dst.write(chunk) + await dst.drain() + except (ConnectionResetError, BrokenPipeError, OSError, + asyncio.CancelledError): + pass + finally: + try: + dst.close() + await dst.wait_closed() + except Exception: + pass + + await asyncio.gather( + _relay(reader, up_writer), + _relay(up_reader, writer), + ) + except Exception: + pass + finally: + try: + up_writer.close() + except Exception: + pass diff --git a/proxy/stats.py b/proxy/stats.py index 3a9e61b..7a7276d 100644 --- a/proxy/stats.py +++ b/proxy/stats.py @@ -8,6 +8,7 @@ class _Stats: self.connections_tcp_fallback = 0 self.connections_cfproxy = 0 self.connections_bad = 0 + self.connections_masked = 0 self.ws_errors = 0 self.bytes_up = 0 self.bytes_down = 0 @@ -24,6 +25,7 @@ class _Stats: f"tcp_fb={self.connections_tcp_fallback} " f"cf={self.connections_cfproxy} " f"bad={self.connections_bad} " + f"masked={self.connections_masked} " f"err={self.ws_errors} " f"pool={pool_s} " f"up={human_bytes(self.bytes_up)} " diff --git a/proxy/tg_ws_proxy.py b/proxy/tg_ws_proxy.py index a2b8a31..900fc16 100644 --- a/proxy/tg_ws_proxy.py +++ b/proxy/tg_ws_proxy.py @@ -28,6 +28,7 @@ from .stats import stats from .config import proxy_config, parse_dc_ip_list, start_cfproxy_domain_refresh, CFPROXY_DEFAULT_DOMAINS from .bridge import MsgSplitter, CryptoCtx, do_fallback, bridge_ws_reencrypt from .raw_websocket import RawWebSocket, WsHandshakeError, set_sock_opts +from .fake_tls import proxy_to_masking_domain, verify_client_hello, build_server_hello, FakeTlsStream, TLS_RECORD_HANDSHAKE log = logging.getLogger('tg-mtproto-proxy') @@ -214,25 +215,115 @@ async def _handle_client(reader, writer, secret: bytes): set_sock_opts(writer.transport, proxy_config.buffer_size) + tls_stream = None + masking = proxy_config.fake_tls_domain + try: + if proxy_config.proxy_protocol: + try: + pp_line = await asyncio.wait_for( + reader.readline(), timeout=10) + except asyncio.IncompleteReadError: + log.debug("[%s] disconnected during PROXY header", label) + return + pp_text = pp_line.decode('ascii', errors='replace').strip() + if pp_text.startswith('PROXY '): + parts = pp_text.split() + if len(parts) >= 6: + label = f"{parts[2]}:{parts[4]}" + log.debug("[%s] PROXY protocol: %s", label, pp_text) + else: + log.debug("[%s] expected PROXY header, got: %r", label, + pp_text[:60]) + try: - handshake = await asyncio.wait_for( - reader.readexactly(HANDSHAKE_LEN), timeout=10) + first_byte = await asyncio.wait_for( + reader.readexactly(1), timeout=10) except asyncio.IncompleteReadError: log.debug("[%s] client disconnected before handshake", label) return + if first_byte[0] == TLS_RECORD_HANDSHAKE and masking: + try: + hdr_rest = await asyncio.wait_for( + reader.readexactly(4), timeout=10) + except asyncio.IncompleteReadError: + log.debug("[%s] incomplete TLS record header", label) + return + + tls_header = first_byte + hdr_rest + record_len = struct.unpack('>H', tls_header[3:5])[0] + + try: + record_body = await asyncio.wait_for( + reader.readexactly(record_len), timeout=10) + except asyncio.IncompleteReadError: + log.debug("[%s] incomplete TLS record body", label) + return + + client_hello = tls_header + record_body + + tls_result = verify_client_hello(client_hello, secret) + + if tls_result is None: + log.debug("[%s] Fake TLS verify failed (size=%d rec=%d) " + "-> masking", + label, len(client_hello), record_len) + await proxy_to_masking_domain( + reader, writer, client_hello, masking, label) + return + + client_random, session_id, ts = tls_result + log.debug("[%s] Fake TLS handshake ok (ts=%d)", label, ts) + + server_hello = build_server_hello(secret, client_random, session_id) + writer.write(server_hello) + await writer.drain() + + tls_stream = FakeTlsStream(reader, writer) + + try: + handshake = await asyncio.wait_for( + tls_stream.readexactly(HANDSHAKE_LEN), timeout=10) + except asyncio.IncompleteReadError: + log.debug("[%s] incomplete obfs2 init inside TLS", label) + return + elif masking: + log.debug("[%s] non-TLS byte 0x%02X -> HTTP redirect", label, + first_byte[0]) + redirect = ( + f"HTTP/1.1 301 Moved Permanently\r\n" + f"Location: https://{masking}/\r\n" + f"Content-Length: 0\r\n" + f"Connection: close\r\n\r\n" + ).encode() + writer.write(redirect) + await writer.drain() + return + else: + try: + rest = await asyncio.wait_for( + reader.readexactly(HANDSHAKE_LEN - 1), timeout=10) + except asyncio.IncompleteReadError: + log.debug("[%s] client disconnected before handshake", label) + return + handshake = first_byte + rest + result = _try_handshake(handshake, secret) if result is None: stats.connections_bad += 1 log.debug("[%s] bad handshake (wrong secret or proto)", label) try: - while await reader.read(4096): + drain_src = tls_stream or reader + while await drain_src.read(4096): pass except Exception: pass return + clt_reader = tls_stream or reader + clt_writer = tls_stream or writer + dc, is_media, proto_tag, client_dec_prekey_iv = result if proto_tag == PROTO_TAG_ABRIDGED: @@ -308,7 +399,7 @@ async def _handle_client(reader, writer, secret: bytes): except Exception: pass ok = await do_fallback( - reader, writer, relay_init, label, + clt_reader, clt_writer, relay_init, label, dc, is_media, media_tag, ctx, splitter=splitter) if not ok: @@ -378,7 +469,7 @@ async def _handle_client(reader, writer, secret: bytes): except Exception: pass ok = await do_fallback( - reader, writer, relay_init, label, + clt_reader, clt_writer, relay_init, label, dc, is_media, media_tag, ctx, splitter=splitter_fb) if ok: @@ -399,7 +490,7 @@ async def _handle_client(reader, writer, secret: bytes): await ws.send(relay_init) - await bridge_ws_reencrypt(reader, writer, ws, label, + await bridge_ws_reencrypt(clt_reader, clt_writer, ws, label, dc=dc, is_media=is_media, ctx=ctx, splitter=splitter) @@ -422,6 +513,7 @@ async def _handle_client(reader, writer, secret: bytes): stats.connections_active -= 1 try: writer.close() + await writer.wait_closed() except BaseException: pass @@ -467,12 +559,23 @@ async def _run(stop_event: Optional[asyncio.Event] = None): pass link_host = get_link_host(proxy_config.host) - tg_link = f"tg://proxy?server={link_host}&port={proxy_config.port}&secret=dd{proxy_config.secret}" + ftls = proxy_config.fake_tls_domain + dd_link = (f"tg://proxy?server={link_host}" + f"&port={proxy_config.port}" + f"&secret=dd{proxy_config.secret}") + ee_link = "" + if ftls: + domain_hex = ftls.encode('ascii').hex() + ee_link = (f"tg://proxy?server={link_host}" + f"&port={proxy_config.port}" + f"&secret=ee{proxy_config.secret}{domain_hex}") log.info("=" * 60) log.info(" Telegram MTProto WS Bridge Proxy") log.info(" Listening on %s:%d", proxy_config.host, proxy_config.port) log.info(" Secret: %s", proxy_config.secret) + if ftls: + log.info(" Fake TLS: %s", ftls) log.info(" Target DC IPs:") for dc in sorted(proxy_config.dc_redirects.keys()): ip = proxy_config.dc_redirects.get(dc) @@ -482,8 +585,12 @@ async def _run(stop_event: Optional[asyncio.Event] = None): user_domain = "user" if proxy_config.cfproxy_user_domain else "auto" log.info(" CF proxy: enabled (%s | %s)", prio, user_domain) log.info("=" * 60) - log.info(" Connect link:") - log.info(" %s", tg_link) + log.info(" Connect links:") + if ftls: + log.info(" ee (Fake TLS): %s", ee_link) + else: + log.info(" (standard): %s", proxy_config.secret) + log.info(" dd (random padding): %s", dd_link) log.info("=" * 60) async def log_stats(): @@ -569,6 +676,13 @@ def main(): help='Disable Cloudflare proxy fallback') ap.add_argument('--cfproxy-priority', type=bool, default=True, help='Try cfproxy before tcp fallback (default: true)') + ap.add_argument('--fake-tls-domain', type=str, default='', + metavar='DOMAIN', + help='Enable Fake TLS (ee-secret) masking with the given ' + 'SNI domain, e.g. example.com') + ap.add_argument('--proxy-protocol', action='store_true', + help='Accept PROXY protocol v1 header ' + '(for use behind nginx/haproxy with proxy_protocol on)') args = ap.parse_args() if not args.dc_ip: @@ -603,6 +717,8 @@ def main(): proxy_config.fallback_cfproxy = not args.no_cfproxy proxy_config.fallback_cfproxy_priority = args.cfproxy_priority proxy_config.cfproxy_user_domain = args.cfproxy_domain + proxy_config.fake_tls_domain = args.fake_tls_domain.strip() + proxy_config.proxy_protocol = args.proxy_protocol log_level = logging.DEBUG if args.verbose else logging.INFO log_fmt = logging.Formatter('%(asctime)s %(levelname)-5s %(message)s', @@ -624,6 +740,8 @@ def main(): fh.setFormatter(log_fmt) root.addHandler(fh) + logging.getLogger('asyncio').setLevel(logging.WARNING) + try: asyncio.run(_run()) except KeyboardInterrupt: diff --git a/utils/tray_common.py b/utils/tray_common.py index 53facff..ed559fd 100644 --- a/utils/tray_common.py +++ b/utils/tray_common.py @@ -153,6 +153,7 @@ def setup_logging(verbose: bool = False, log_max_mb: float = 5) -> None: level = logging.DEBUG if verbose else logging.INFO root = logging.getLogger() root.setLevel(level) + logging.getLogger('asyncio').setLevel(logging.WARNING) fh = logging.handlers.RotatingFileHandler( str(LOG_FILE),