From 3cb1929dc8570ef56e8ef2a3168c676e65b6ccc9 Mon Sep 17 00:00:00 2001 From: Flowseal Date: Mon, 23 Mar 2026 04:30:38 +0300 Subject: [PATCH 1/8] removed test script --- stress_test.py | 246 ------------------------------------------------- 1 file changed, 246 deletions(-) delete mode 100644 stress_test.py diff --git a/stress_test.py b/stress_test.py deleted file mode 100644 index f2fd88c..0000000 --- a/stress_test.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -Stress-test: сравнение OLD vs NEW реализаций горячих функций прокси. - -Тестируются: - 1. _build_frame — сборка WS-фрейма (masked binary) - 2. _build_frame — сборка WS-фрейма (unmasked) - 3. _socks5_reply — генерация SOCKS5-ответа - 4. _dc_from_init XOR-часть (bytes(a^b for …) vs int.from_bytes) - 5. mask key generation (os.urandom vs PRNG) -""" - -import gc -import os -import random -import struct -import time - -# ── Размеры данных, типичные для Telegram ────────────────────────── -SMALL = 64 # init-пакет / ack -MEDIUM = 1024 # текстовое сообщение -LARGE = 65536 # фото / голосовое - - -# ═══════════════════════════════════════════════════════════════════ -# XOR mask (не менялся — для полноты) -# ═══════════════════════════════════════════════════════════════════ - -def xor_mask(data: bytes, mask: bytes) -> bytes: - if not data: - return data - n = len(data) - mask_rep = (mask * (n // 4 + 1))[:n] - return (int.from_bytes(data, 'big') ^ int.from_bytes(mask_rep, 'big')).to_bytes(n, 'big') - - -# ═══════════════════════════════════════════════════════════════════ -# _build_frame -# ═══════════════════════════════════════════════════════════════════ - -def build_frame_old(opcode: int, data: bytes, mask: bool = False) -> bytes: - """Старая: bytearray + append/extend + os.urandom.""" - header = bytearray() - header.append(0x80 | opcode) - length = len(data) - mask_bit = 0x80 if mask else 0x00 - - if length < 126: - header.append(mask_bit | length) - elif length < 65536: - header.append(mask_bit | 126) - header.extend(struct.pack('>H', length)) - else: - header.append(mask_bit | 127) - header.extend(struct.pack('>Q', length)) - - if mask: - mask_key = os.urandom(4) - header.extend(mask_key) - return bytes(header) + xor_mask(data, mask_key) - return bytes(header) + data - - -# ── Новая: pre-compiled struct + PRNG ────────────────────────────── -_st_BB = struct.Struct('>BB') -_st_BBH = struct.Struct('>BBH') -_st_BBQ = struct.Struct('>BBQ') -_st_BB4s = struct.Struct('>BB4s') -_st_BBH4s = struct.Struct('>BBH4s') -_st_BBQ4s = struct.Struct('>BBQ4s') - -_mask_rng = random.Random(int.from_bytes(os.urandom(16), 'big')) -_mask_pack = struct.Struct('>I').pack - -def _random_mask_key() -> bytes: - return _mask_pack(_mask_rng.getrandbits(32)) - -def build_frame_new(opcode: int, data: bytes, mask: bool = False) -> bytes: - """Новая: struct.pack + PRNG mask.""" - length = len(data) - fb = 0x80 | opcode - - if not mask: - if length < 126: - return _st_BB.pack(fb, length) + data - if length < 65536: - return _st_BBH.pack(fb, 126, length) + data - return _st_BBQ.pack(fb, 127, length) + data - - mask_key = _random_mask_key() - masked = xor_mask(data, mask_key) - if length < 126: - return _st_BB4s.pack(fb, 0x80 | length, mask_key) + masked - if length < 65536: - return _st_BBH4s.pack(fb, 0x80 | 126, length, mask_key) + masked - return _st_BBQ4s.pack(fb, 0x80 | 127, length, mask_key) + masked - - -# ═══════════════════════════════════════════════════════════════════ -# _socks5_reply -# ═══════════════════════════════════════════════════════════════════ - -def socks5_reply_old(status): - return bytes([0x05, status, 0x00, 0x01]) + b'\x00' * 6 - -_SOCKS5_REPLIES = {s: bytes([0x05, s, 0x00, 0x01, 0, 0, 0, 0, 0, 0]) - for s in (0x00, 0x05, 0x07, 0x08)} - -def socks5_reply_new(status): - return _SOCKS5_REPLIES[status] - - -# ═══════════════════════════════════════════════════════════════════ -# dc_from_init XOR (8 байт keystream ^ data) -# ═══════════════════════════════════════════════════════════════════ - -def dc_xor_old(data8: bytes, ks8: bytes) -> bytes: - """Старая: генераторное выражение.""" - return bytes(a ^ b for a, b in zip(data8, ks8)) - -def dc_xor_new(data8: bytes, ks8: bytes) -> bytes: - """Новая: int.from_bytes.""" - return (int.from_bytes(data8, 'big') ^ int.from_bytes(ks8, 'big')).to_bytes(8, 'big') - - -# ═══════════════════════════════════════════════════════════════════ -# mask key: os.urandom(4) vs PRNG -# ═══════════════════════════════════════════════════════════════════ - -def mask_key_old() -> bytes: - return os.urandom(4) - -def mask_key_new() -> bytes: - return _random_mask_key() - - -# ═══════════════════════════════════════════════════════════════════ -# Бенчмарк -# ═══════════════════════════════════════════════════════════════════ - -def bench(func, args_list: list, iters: int) -> float: - gc.collect() - for i in range(min(100, iters)): - func(*args_list[i % len(args_list)]) - start = time.perf_counter() - for i in range(iters): - func(*args_list[i % len(args_list)]) - elapsed = time.perf_counter() - start - return elapsed / iters * 1_000_000 # мкс - - -def compare(name: str, old_fn, new_fn, args_list: list, iters: int): - t_old = bench(old_fn, args_list, iters) - t_new = bench(new_fn, args_list, iters) - speedup = t_old / t_new if t_new > 0 else float('inf') - marker = '✅' if speedup >= 1.0 else '⚠️' - print(f" {name:.<42s} OLD {t_old:8.3f} мкс | NEW {t_new:8.3f} мкс | {speedup:5.2f}x {marker}") - - -# ═══════════════════════════════════════════════════════════════════ - -def main(): - print("=" * 74) - print(" Stress Test: OLD vs NEW (горячие функции tg_ws_proxy)") - print("=" * 74) - - N = 500_000 - - # # ── 1. _build_frame masked ──────────────────────────────────── - # print(f"\n── _build_frame masked ({N:,} итераций) ──") - # for size, label in [(SMALL, "64B"), (MEDIUM, "1KB"), (LARGE, "64KB")]: - # data_list = [(0x2, os.urandom(size), True) for _ in range(1000)] - # compare(f"build_frame masked {label}", - # build_frame_old, build_frame_new, data_list, N) - - # # ── 2. _build_frame unmasked ────────────────────────────────── - # print(f"\n── _build_frame unmasked ({N:,} итераций) ──") - # for size, label in [(SMALL, "64B"), (MEDIUM, "1KB"), (LARGE, "64KB")]: - # data_list = [(0x2, os.urandom(size), False) for _ in range(1000)] - # compare(f"build_frame unmasked {label}", - # build_frame_old, build_frame_new, data_list, N) - - # # ── 3. mask key generation ──────────────────────────────────── - # print(f"\n── mask key: os.urandom(4) vs PRNG ({N:,} итераций) ──") - # compare("mask_key", mask_key_old, mask_key_new, [()] * 100, N) - - # # ── 4. _socks5_reply ───────────────────────────────────────── - N2 = 2_000_000 - # print(f"\n── _socks5_reply ({N2:,} итераций) ──") - # compare("socks5_reply", socks5_reply_old, socks5_reply_new, - # [(s,) for s in (0x00, 0x05, 0x07, 0x08)], N2) - - # # ── 5. dc_from_init XOR (8 bytes) ──────────────────────────── - # print(f"\n── dc_xor 8B: generator vs int.from_bytes ({N2:,} итераций) ──") - # compare("dc_xor_8B", dc_xor_old, dc_xor_new, - # [(os.urandom(8), os.urandom(8)) for _ in range(1000)], N2) - - # ── 6. _read_frame struct.unpack vs pre-compiled ───────────── - print(f"\n── struct unpack read-path ({N2:,} итераций) ──") - _st_H_pre = struct.Struct('>H') - _st_Q_pre = struct.Struct('>Q') - h_bufs = [(os.urandom(2),) for _ in range(1000)] - q_bufs = [(os.urandom(8),) for _ in range(1000)] - compare("unpack >H", - lambda b: struct.unpack('>H', b), - lambda b: _st_H_pre.unpack(b), - h_bufs, N2) - compare("unpack >Q", - lambda b: struct.unpack('>Q', b), - lambda b: _st_Q_pre.unpack(b), - q_bufs, N2) - - # ── 7. dc_from_init: 2x unpack vs 1x merged ───────────────── - print(f"\n── dc_from_init unpack: 2 calls vs 1 merged ({N2:,} итераций) ──") - _st_Ih = struct.Struct(' Date: Mon, 23 Mar 2026 12:38:35 +0700 Subject: [PATCH 2/8] build: win7 32bit support (#298) --- .github/workflows/build.yml | 55 +++++++++++++++++++++++++++++++------ README.md | 3 +- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0721ad9..0cb1ffa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,7 @@ jobs: path: | dist/TgWsProxy_windows.exe - build-win7: + build-win7-64bit: runs-on: windows-latest steps: - name: Checkout @@ -58,6 +58,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.8" + architecture: "x64" cache: "pip" - name: Install dependencies (Win7-compatible) @@ -70,13 +71,44 @@ jobs: run: pyinstaller packaging/windows.spec --noconfirm - name: Rename artifact - run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7.exe + run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7_64bit.exe - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: TgWsProxy-win7 - path: dist/TgWsProxy_windows_7.exe + name: TgWsProxy-win7-64bit + path: dist/TgWsProxy_windows_7_64bit.exe + + build-win7-32bit: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python 3.8 32-bit (Win7 x86) + uses: actions/setup-python@v5 + with: + python-version: "3.8" + architecture: "x86" + cache: "pip" + + - name: Install dependencies (Win7 x86) + run: pip install ".[win7]" + + - name: Install PyInstaller (Win7 x86) + run: pip install "pyinstaller==5.13.2" + + - name: Build EXE with PyInstaller (Win7 32-bit) + run: pyinstaller packaging/windows.spec --noconfirm + + - name: Rename artifact + run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7_32bit.exe + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: TgWsProxy-win7-32bit + path: dist/TgWsProxy_windows_7_32bit.exe build-macos: runs-on: macos-latest @@ -317,7 +349,7 @@ jobs: dist/TgWsProxy_linux_amd64.deb release: - needs: [build, build-win7, build-macos, build-linux] + needs: [build, build-win7-64bit, build-win7-32bit, build-macos, build-linux] runs-on: ubuntu-latest if: ${{ github.event.inputs.make_release == 'true' }} steps: @@ -327,10 +359,16 @@ jobs: name: TgWsProxy path: dist - - name: Download Win7 build + - name: Download Win7 (64bit) build uses: actions/download-artifact@v4 with: - name: TgWsProxy-win7 + name: TgWsProxy-win7-64bit + path: dist + + - name: Download Win7 (32bit) build + uses: actions/download-artifact@v4 + with: + name: TgWsProxy-win7-32bit path: dist - name: Download macOS build @@ -354,7 +392,8 @@ jobs: ## TG WS Proxy ${{ github.event.inputs.version }} files: | dist/TgWsProxy_windows.exe - dist/TgWsProxy_windows_7.exe + dist/TgWsProxy_windows_7_64bit.exe + dist/TgWsProxy_windows_7_32bit.exe dist/TgWsProxy_macos_universal.dmg dist/TgWsProxy_linux_amd64 dist/TgWsProxy_linux_amd64.deb diff --git a/README.md b/README.md index 984ce40..799a237 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,8 @@ Tray-приложение хранит данные в: Минимально поддерживаемые версии ОС для текущих бинарных сборок: - Windows 10+ для `TgWsProxy_windows.exe` -- Windows 7 для `TgWsProxy_windows_7.exe` +- Windows 7 (x64) для `TgWsProxy_windows_7_64bit.exe` +- Windows 7 (x32) для `TgWsProxy_windows_7_32bit.exe` - Intel macOS 10.15+ - Apple Silicon macOS 11.0+ - Linux x86_64 (требуется AppIndicator для системного трея) From e1004e5e73e876bb538962908184b45ed608f281 Mon Sep 17 00:00:00 2001 From: xdshkaaa <83063989+pyfig@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:39:00 +0700 Subject: [PATCH 3/8] Fix macOS settings dialog cancellation flow (#392) --- macos.py | 60 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/macos.py b/macos.py index 8241b38..46eb5cf 100644 --- a/macos.py +++ b/macos.py @@ -221,6 +221,10 @@ def _ensure_menubar_icon(): # Native macOS dialogs +def _escape_osascript_text(text: str) -> str: + return text.replace('\\', '\\\\').replace('"', '\\"') + + def _osascript(script: str) -> str: r = subprocess.run( ['osascript', '-e', script], @@ -229,28 +233,46 @@ def _osascript(script: str) -> str: def _show_error(text: str, title: str = "TG WS Proxy"): - text_esc = text.replace('\\', '\\\\').replace('"', '\\"') - title_esc = title.replace('\\', '\\\\').replace('"', '\\"') + text_esc = _escape_osascript_text(text) + title_esc = _escape_osascript_text(title) _osascript( f'display dialog "{text_esc}" with title "{title_esc}" ' f'buttons {{"OK"}} default button "OK" with icon stop') def _show_info(text: str, title: str = "TG WS Proxy"): - text_esc = text.replace('\\', '\\\\').replace('"', '\\"') - title_esc = title.replace('\\', '\\\\').replace('"', '\\"') + text_esc = _escape_osascript_text(text) + title_esc = _escape_osascript_text(title) _osascript( f'display dialog "{text_esc}" with title "{title_esc}" ' f'buttons {{"OK"}} default button "OK" with icon note') def _ask_yes_no(text: str, title: str = "TG WS Proxy") -> bool: - text_esc = text.replace('\\', '\\\\').replace('"', '\\"') - title_esc = title.replace('\\', '\\\\').replace('"', '\\"') - result = _osascript( - f'display dialog "{text_esc}" with title "{title_esc}" ' - f'buttons {{"Нет", "Да"}} default button "Да" with icon note') - return "Да" in result + result = _ask_yes_no_close(text, title) + return result is True + + +def _ask_yes_no_close(text: str, + title: str = "TG WS Proxy") -> Optional[bool]: + text_esc = _escape_osascript_text(text) + title_esc = _escape_osascript_text(title) + r = subprocess.run( + ['osascript', '-e', + f'button returned of (display dialog "{text_esc}" ' + f'with title "{title_esc}" ' + f'buttons {{"Закрыть", "Нет", "Да"}} ' + f'default button "Да" cancel button "Закрыть" with icon note)'], + capture_output=True, text=True) + if r.returncode != 0: + return None + + result = r.stdout.strip() + if result == "Да": + return True + if result == "Нет": + return False + return None # Proxy lifecycle @@ -383,15 +405,16 @@ def _on_open_logs(_=None): # Show a native text input dialog. Returns None if cancelled. def _osascript_input(prompt: str, default: str, title: str = "TG WS Proxy") -> Optional[str]: - prompt_esc = prompt.replace('\\', '\\\\').replace('"', '\\"') - default_esc = default.replace('\\', '\\\\').replace('"', '\\"') - title_esc = title.replace('\\', '\\\\').replace('"', '\\"') + prompt_esc = _escape_osascript_text(prompt) + default_esc = _escape_osascript_text(default) + title_esc = _escape_osascript_text(title) r = subprocess.run( ['osascript', '-e', f'text returned of (display dialog "{prompt_esc}" ' f'default answer "{default_esc}" ' f'with title "{title_esc}" ' - f'buttons {{"Отмена", "OK"}} default button "OK")'], + f'buttons {{"Закрыть", "OK"}} ' + f'default button "OK" cancel button "Закрыть")'], capture_output=True, text=True) if r.returncode != 0: return None @@ -452,7 +475,9 @@ def _edit_config_dialog(): return # Verbose - verbose = _ask_yes_no("Включить подробное логирование (verbose)?") + verbose = _ask_yes_no_close("Включить подробное логирование (verbose)?") + if verbose is None: + return # Advanced settings adv_str = _osascript_input( @@ -461,6 +486,8 @@ def _edit_config_dialog(): f"{cfg.get('buf_kb', DEFAULT_CONFIG['buf_kb'])}," f"{cfg.get('pool_size', DEFAULT_CONFIG['pool_size'])}," f"{cfg.get('log_max_mb', DEFAULT_CONFIG['log_max_mb'])}") + if adv_str is None: + return adv = {} if adv_str: @@ -491,7 +518,8 @@ def _edit_config_dialog(): if _app: _app.update_menu_title() - if _ask_yes_no("Настройки сохранены.\n\nПерезапустить прокси сейчас?"): + if _ask_yes_no_close( + "Настройки сохранены.\n\nПерезапустить прокси сейчас?"): restart_proxy() From e3d4578eed7ab18d25acee611b817dab8fe1e273 Mon Sep 17 00:00:00 2001 From: gogamlg3 <53863382+gogamlg3@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:39:29 +0500 Subject: [PATCH 4/8] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BF=D0=BE=D1=81=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20AUR=20=D0=B4=D0=BB=D1=8F=20Arc?= =?UTF-8?q?h=20=D0=B4=D0=B8=D1=81=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82?= =?UTF-8?q?=D0=B8=D0=B2=D0=BE=D0=B2=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 799a237..6b90841 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,21 @@ Telegram Desktop → SOCKS5 (127.0.0.1:1080) → TG WS Proxy → WSS → Telegra Для Debian/Ubuntu скачайте со [страницы релизов](https://github.com/Flowseal/tg-ws-proxy/releases) пакет **`TgWsProxy_linux_amd64.deb`**. +Для Arch и Arch-Based дистрибутивов подготовлены пакеты в AUR: [tg-ws-proxy-bin](https://aur.archlinux.org/packages/tg-ws-proxy-bin), [tg-ws-proxy-git](https://aur.archlinux.org/packages/tg-ws-proxy-git), [tg-ws-proxy-cli](https://aur.archlinux.org/packages/tg-ws-proxy-cli) + +```shell +# Установка без AUR-helper +git clone https://aur.archlinux.org/tg-ws-proxy-bin.git +cd tg-ws-proxy-bin +makepkg -si + +# При помощи AUR-helper +paru -S tg-ws-proxy-bin + +# Если вы установили -cli пакет, то запуск осуществляется через systemctl, где 8888 это номер порта прокси: +sudo systemctl start tg-ws-proxy-cli@8888 +``` + Для остальных дистрибутивов можно использовать **`TgWsProxy_linux_amd64`** (бинарный файл для x86_64). ```bash From f3d05f7efcb4fb03573fb4b529f5ec597c4dc647 Mon Sep 17 00:00:00 2001 From: delewer <108271242+IMDelewer@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:09:30 +0700 Subject: [PATCH 5/8] chore: pyproject optimization (#292) --- .github/workflows/build.yml | 8 +++--- README.md | 15 +++-------- proxy/__init__.py | 2 +- pyproject.toml | 54 +++++++++++-------------------------- 4 files changed, 25 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cb1ffa..ce7bc08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: cache: "pip" - name: Install dependencies - run: pip install ".[win10]" + run: pip install . - name: Install pyinstaller run: pip install "pyinstaller==6.13.0" @@ -62,7 +62,7 @@ jobs: cache: "pip" - name: Install dependencies (Win7-compatible) - run: pip install ".[win7]" + run: pip install . - name: Install pyinstaller run: pip install "pyinstaller==5.13.2" @@ -174,7 +174,7 @@ jobs: -w wheelhouse/universal2 python3.12 -m pip install --no-deps wheelhouse/universal2/*.whl - python3.12 -m pip install ".[macos]" + python3.12 -m pip install . python3.12 -m pip install pyinstaller==6.13.0 - name: Create macOS icon from ICO @@ -276,7 +276,7 @@ jobs: - name: Install dependencies run: | .venv/bin/pip install --upgrade pip - .venv/bin/pip install ".[linux]" + .venv/bin/pip install . .venv/bin/pip install "pyinstaller==6.13.0" - name: Build binary with PyInstaller diff --git a/README.md b/README.md index 6b90841..a44043b 100644 --- a/README.md +++ b/README.md @@ -91,31 +91,24 @@ pip install -e . tg-ws-proxy ``` -### Windows 10+ +### Windows 7/10+ ```bash -pip install -e ".[win10]" -tg-ws-proxy-tray-win -``` - -### Windows 7 - -```bash -pip install -e ".[win7]" +pip install -e . tg-ws-proxy-tray-win ``` ### macOS ```bash -pip install -e ".[macos]" +pip install -e . tg-ws-proxy-tray-macos ``` ### Linux ```bash -pip install -e ".[linux]" +pip install -e . tg-ws-proxy-tray-linux ``` diff --git a/proxy/__init__.py b/proxy/__init__.py index f74f503..9e2406e 100644 --- a/proxy/__init__.py +++ b/proxy/__init__.py @@ -1 +1 @@ -__version__ = "1.1.3" \ No newline at end of file +__version__ = "1.3.0" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bbd9c1a..0524036 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,59 +18,37 @@ authors = [ keywords = [ "telegram", + "tdesktop", "proxy", - "websocket" + "bypass", + "websocket", + "socks5", ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", - "Environment :: MacOS X :: Cocoa", - "Environment :: Win32 (MS Windows)", - "Environment :: X11 Applications :: GTK", "Intended Audience :: Customer Service", "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", + "Operating System :: OS Independent", "Topic :: System :: Networking :: Firewalls", ] dependencies = [ + "pyperclip==1.9.0", + + "psutil==5.9.8; platform_system == 'Windows' and python_version < '3.9'", "cryptography==41.0.7; platform_system == 'Windows' and python_version < '3.9'", + "Pillow==10.4.0; platform_system == 'Windows' and python_version < '3.9'", + + "psutil==7.0.0; platform_system != 'Windows' or python_version >= '3.9'", "cryptography==46.0.5; platform_system != 'Windows' or python_version >= '3.9'", -] + "Pillow==12.1.1; (platform_system != 'Windows' or python_version >= '3.9') and platform_system != 'Darwin'", -[project.optional-dependencies] -win7 = [ - "customtkinter==5.2.2", - "Pillow==10.4.0", - "psutil==5.9.8", - "pystray==0.19.5", - "pyperclip==1.9.0", -] - -win10 = [ - "customtkinter==5.2.2", - "Pillow==12.1.1", - "psutil==7.0.0", - "pystray==0.19.5", - "pyperclip==1.9.0", -] - -macos = [ - "Pillow==12.1.0", - "psutil==7.0.0", - "pyperclip==1.9.0", - "rumps==0.4.0", -] - -linux = [ - "customtkinter==5.2.2", - "Pillow==12.1.1", - "psutil==7.0.0", - "pystray==0.19.5", - "pyperclip==1.9.0", + "customtkinter==5.2.2; platform_system != 'Darwin'", + "pystray==0.19.5; platform_system != 'Darwin'", + "rumps==0.4.0; platform_system == 'Darwin'", + "Pillow==12.1.0; platform_system == 'Darwin'", ] [project.scripts] From f95b9b7da00236bda91b7d9f6bc21fe73886db58 Mon Sep 17 00:00:00 2001 From: Flowseal Date: Mon, 23 Mar 2026 09:12:06 +0300 Subject: [PATCH 6/8] Update win7 build according to #292 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce7bc08..803a852 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -93,7 +93,7 @@ jobs: cache: "pip" - name: Install dependencies (Win7 x86) - run: pip install ".[win7]" + run: pip install . - name: Install PyInstaller (Win7 x86) run: pip install "pyinstaller==5.13.2" From c0183bf448ecf36c816cc6af79223f7f599c9098 Mon Sep 17 00:00:00 2001 From: KG7x Date: Mon, 23 Mar 2026 17:10:20 +0300 Subject: [PATCH 7/8] Fix warn Node24 actions update & Simplify build (#410) --- .github/workflows/build.yml | 116 ++++++++++-------------------------- 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 803a852..8e00a50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,10 +21,10 @@ jobs: runs-on: windows-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.12" cache: "pip" @@ -42,79 +42,49 @@ jobs: run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows.exe - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: TgWsProxy - path: | - dist/TgWsProxy_windows.exe + path: dist/TgWsProxy_windows.exe - build-win7-64bit: + build-win7: runs-on: windows-latest + strategy: + matrix: + include: + - arch: x64 + suffix: 64bit + - arch: x86 + suffix: 32bit steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - name: Setup Python 3.8 (last version supporting Win7) - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.8" - architecture: "x64" + architecture: ${{ matrix.arch }} cache: "pip" + + - name: Install dependencies & pyinstaller + run: pip install . "pyinstaller==5.13.2" - - name: Install dependencies (Win7-compatible) - run: pip install . - - - name: Install pyinstaller - run: pip install "pyinstaller==5.13.2" - - - name: Build EXE with PyInstaller (Win7) + - name: Build EXE with PyInstaller run: pyinstaller packaging/windows.spec --noconfirm - name: Rename artifact - run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7_64bit.exe + run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7_${{ matrix.suffix }}.exe - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - name: TgWsProxy-win7-64bit - path: dist/TgWsProxy_windows_7_64bit.exe - - build-win7-32bit: - runs-on: windows-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Python 3.8 32-bit (Win7 x86) - uses: actions/setup-python@v5 - with: - python-version: "3.8" - architecture: "x86" - cache: "pip" - - - name: Install dependencies (Win7 x86) - run: pip install . - - - name: Install PyInstaller (Win7 x86) - run: pip install "pyinstaller==5.13.2" - - - name: Build EXE with PyInstaller (Win7 32-bit) - run: pyinstaller packaging/windows.spec --noconfirm - - - name: Rename artifact - run: mv dist/TgWsProxy.exe dist/TgWsProxy_windows_7_32bit.exe - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: TgWsProxy-win7-32bit - path: dist/TgWsProxy_windows_7_32bit.exe + name: TgWsProxy-win7-${{ matrix.suffix }} + path: dist/TgWsProxy_windows_7_${{ matrix.suffix }}.exe build-macos: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install universal2 Python run: | @@ -249,7 +219,7 @@ jobs: rm -rf "$DMG_TEMP" - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: TgWsProxy-macOS path: dist/TgWsProxy_macos_universal.dmg @@ -258,7 +228,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install system dependencies run: | @@ -341,7 +311,7 @@ jobs: "dist/TgWsProxy_linux_amd64.deb" - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: TgWsProxy-linux path: | @@ -349,39 +319,15 @@ jobs: dist/TgWsProxy_linux_amd64.deb release: - needs: [build, build-win7-64bit, build-win7-32bit, build-macos, build-linux] + needs: [build, build-win7, build-macos, build-linux] runs-on: ubuntu-latest if: ${{ github.event.inputs.make_release == 'true' }} steps: - - name: Download main build - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v4 with: - name: TgWsProxy - path: dist - - - name: Download Win7 (64bit) build - uses: actions/download-artifact@v4 - with: - name: TgWsProxy-win7-64bit - path: dist - - - name: Download Win7 (32bit) build - uses: actions/download-artifact@v4 - with: - name: TgWsProxy-win7-32bit - path: dist - - - name: Download macOS build - uses: actions/download-artifact@v4 - with: - name: TgWsProxy-macOS - path: dist - - - name: Download Linux build - uses: actions/download-artifact@v4 - with: - name: TgWsProxy-linux + pattern: TgWsProxy* path: dist + merge-multiple: true - name: Create GitHub Release uses: softprops/action-gh-release@v2 From 7a1e2f3f5b1bd88556b198943aa104191166b77d Mon Sep 17 00:00:00 2001 From: KG7x Date: Mon, 23 Mar 2026 18:06:46 +0300 Subject: [PATCH 8/8] Miss update actions/download-artifact (#412) --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e00a50..a44eb7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ permissions: contents: write jobs: - build: + build-windows: runs-on: windows-latest steps: - name: Checkout @@ -319,11 +319,11 @@ jobs: dist/TgWsProxy_linux_amd64.deb release: - needs: [build, build-win7, build-macos, build-linux] + needs: [build-windows, build-win7, build-macos, build-linux] runs-on: ubuntu-latest if: ${{ github.event.inputs.make_release == 'true' }} steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: pattern: TgWsProxy* path: dist