Compare commits

..

8 Commits

Author SHA1 Message Date
Dmitry Alekhine 37deed29ae Merge b66732f7f9 into ca81d037f7 2026-04-14 08:34:38 +03:00
Flowseal ca81d037f7 docs update 2026-04-14 03:11:13 +03:00
Flowseal 07615af49c bootloader build fix 2026-04-14 02:44:15 +03:00
Flowseal f8ee37370d Version bump 2026-04-14 00:27:27 +03:00
Flowseal 4cbb9e555c windows mutex-lock 2026-04-14 00:27:27 +03:00
Flowseal 25ae4b0a24 build version changes 2026-04-14 00:27:27 +03:00
Kleshzz 8af1bc8c89 Add .gitattributes & Update .gitignore (#690) 2026-04-13 19:30:57 +03:00
dmfrpro b66732f7f9 Add NixOS flake options
Signed-off-by: dmfrpro <dmfr2021y@gmail.com>
2026-04-10 03:55:15 +03:00
10 changed files with 116 additions and 20 deletions
+9
View File
@@ -0,0 +1,9 @@
* text=auto eol=lf
*.py text diff=python
*.spec text linguist-language=Python
*.toml text
*.txt text
*.ico binary
+6 -5
View File
@@ -38,10 +38,11 @@ jobs:
run: pip install . run: pip install .
- name: Build PyInstaller bootloader from source - name: Build PyInstaller bootloader from source
run: |
pip install "pyinstaller==6.16.0" --no-binary pyinstaller
env: env:
PYINSTALLER_COMPILE_BOOTLOADER: 1 PYINSTALLER_COMPILE_BOOTLOADER: "1"
run: |
pip download --no-binary pyinstaller --no-deps --no-cache-dir -d pyinstaller_src "pyinstaller==6.10.0"
pip install (Get-ChildItem pyinstaller_src\*.tar.gz).FullName
- name: Build EXE with PyInstaller - name: Build EXE with PyInstaller
run: pyinstaller packaging/windows.spec --noconfirm run: pyinstaller packaging/windows.spec --noconfirm
@@ -193,7 +194,7 @@ jobs:
python3.12 -m pip install --no-deps wheelhouse/universal2/*.whl python3.12 -m pip install --no-deps wheelhouse/universal2/*.whl
python3.12 -m pip install . python3.12 -m pip install .
python3.12 -m pip install pyinstaller==6.16.0 python3.12 -m pip install pyinstaller==6.13.0
- name: Create macOS icon from ICO - name: Create macOS icon from ICO
run: | run: |
@@ -295,7 +296,7 @@ jobs:
run: | run: |
.venv/bin/pip install --upgrade pip .venv/bin/pip install --upgrade pip
.venv/bin/pip install . .venv/bin/pip install .
.venv/bin/pip install "pyinstaller==6.16.0" .venv/bin/pip install "pyinstaller==6.13.0"
- name: Build binary with PyInstaller - name: Build binary with PyInstaller
run: .venv/bin/pyinstaller packaging/linux.spec --noconfirm run: .venv/bin/pyinstaller packaging/linux.spec --noconfirm
+2
View File
@@ -6,6 +6,8 @@ __pycache__/
dist/ dist/
build/ build/
*.spec.bak *.spec.bak
venv/
.venv/
# PyInstaller # PyInstaller
*.manifest *.manifest
+48 -4
View File
@@ -11,13 +11,13 @@
> >
> ### Реакция антивирусов > ### Реакция антивирусов
> >
> Windows Defender часто ошибочно помечает приложение как **Wacatac**. > Антивирусы часто ошибочно помечают приложение как вирус из-за упаковщика.
> Если вы не можете скачать из-за блокировки, то: > Если вы не можете скачать из-за блокировки антивирусом, то:
> >
> 1) Попробуйте скачать версию win7 (она ничем не отличается в плане функционала) > 1) **Попробуйте скачать версию win7 (она ничем не отличается в плане функционала)**
> 2) Отключите антивирус на время скачивания, добавьте файл в исключения и включите обратно > 2) Отключите антивирус на время скачивания, добавьте файл в исключения и включите обратно
> >
> **Всегда проверяйте, что скачиваете из интернета, тем более из непроверенных источников. Всегда лучше смотреть на детекты широко известных антивирусов на VirusTotal** > Всегда проверяйте, что скачиваете из интернета, тем более из непроверенных источников. Всегда лучше смотреть на детекты широко известных антивирусов на VirusTotal
# TG WS Proxy # TG WS Proxy
@@ -112,6 +112,50 @@ paru -S tg-ws-proxy-bin
sudo systemctl start tg-ws-proxy-cli@8888:3075abe65830f0325116bb0416cadf9f sudo systemctl start tg-ws-proxy-cli@8888:3075abe65830f0325116bb0416cadf9f
``` ```
Для NixOS используйте [tg-ws-proxy-flake](https://github.com/dmfrpro/tg-ws-proxy-flake):
```nix
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
tg-ws-proxy.url = "github:dmfrpro/tg-ws-proxy-flake";
tg-ws-proxy.inputs.nixpkgs.follows = "nixpkgs";
};
```
Далее добавляете или системный модуль, или home-manager модуль:
```nix
outputs = { self, nixpkgs, tg-ws-proxy, ... }: {
# Или NixOS модуль
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
tg-ws-proxy.nixosModules.tg-ws-proxy
];
};
# Или Home-manager модуль
homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [
tg-ws-proxy.homeModules.tg-ws-proxy
];
};
};
```
и включаете сервис:
```nix
{
services.tg-ws-proxy = {
enable = true;
secret = "3075abe65830f0325116bb0416cadf9f"; # openssl rand -hex 16
};
}
```
Для остальных дистрибутивов можно использовать **`TgWsProxy_linux_amd64`** (бинарный файл для x86_64). Для остальных дистрибутивов можно использовать **`TgWsProxy_linux_amd64`** (бинарный файл для x86_64).
```bash ```bash
+1 -1
View File
@@ -273,7 +273,7 @@ def run_tray() -> None:
def main() -> None: def main() -> None:
if not acquire_lock("linux.py"): if not acquire_lock():
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0])) _show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return return
try: try:
+1 -1
View File
@@ -610,7 +610,7 @@ def run_menubar() -> None:
def main() -> None: def main() -> None:
if not acquire_lock("macos.py"): if not acquire_lock():
_show_info("Приложение уже запущено.") _show_info("Приложение уже запущено.")
return return
try: try:
+4 -4
View File
@@ -4,8 +4,8 @@
# http://msdn.microsoft.com/en-us/library/ms646997.aspx # http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo( VSVersionInfo(
ffi=FixedFileInfo( ffi=FixedFileInfo(
filevers=(1, 0, 0, 0), filevers=(1, 6, 2, 0),
prodvers=(1, 0, 0, 0), prodvers=(1, 6, 2, 0),
mask=0x3f, mask=0x3f,
flags=0x0, flags=0x0,
OS=0x40004, OS=0x40004,
@@ -21,12 +21,12 @@ VSVersionInfo(
[ [
StringStruct(u'CompanyName', u'Flowseal'), StringStruct(u'CompanyName', u'Flowseal'),
StringStruct(u'FileDescription', u'Telegram Desktop WebSocket Bridge Proxy'), StringStruct(u'FileDescription', u'Telegram Desktop WebSocket Bridge Proxy'),
StringStruct(u'FileVersion', u'1.0.0.0'), StringStruct(u'FileVersion', u'1.6.2.0'),
StringStruct(u'InternalName', u'TgWsProxy'), StringStruct(u'InternalName', u'TgWsProxy'),
StringStruct(u'LegalCopyright', u'Copyright (c) Flowseal. MIT License.'), StringStruct(u'LegalCopyright', u'Copyright (c) Flowseal. MIT License.'),
StringStruct(u'OriginalFilename', u'TgWsProxy.exe'), StringStruct(u'OriginalFilename', u'TgWsProxy.exe'),
StringStruct(u'ProductName', u'TG WS Proxy'), StringStruct(u'ProductName', u'TG WS Proxy'),
StringStruct(u'ProductVersion', u'1.0.0.0'), StringStruct(u'ProductVersion', u'1.6.2.0'),
] ]
) )
] ]
+1 -1
View File
@@ -1,6 +1,6 @@
from .config import parse_dc_ip_list, proxy_config from .config import parse_dc_ip_list, proxy_config
from .utils import get_link_host from .utils import get_link_host
__version__ = "1.6.1" __version__ = "1.6.2"
__all__ = ["__version__", "get_link_host", "proxy_config", "parse_dc_ip_list"] __all__ = ["__version__", "get_link_host", "proxy_config", "parse_dc_ip_list"]
+3 -3
View File
@@ -51,7 +51,7 @@ def ensure_dirs() -> None:
_lock_file_path: Optional[Path] = None _lock_file_path: Optional[Path] = None
def _same_process(meta: dict, proc: psutil.Process, script_hint: str) -> bool: def _same_process(meta: dict, proc: psutil.Process) -> bool:
try: try:
lock_ct = float(meta.get("create_time", 0.0)) lock_ct = float(meta.get("create_time", 0.0))
if lock_ct > 0 and abs(lock_ct - proc.create_time()) > 1.0: if lock_ct > 0 and abs(lock_ct - proc.create_time()) > 1.0:
@@ -63,7 +63,7 @@ def _same_process(meta: dict, proc: psutil.Process, script_hint: str) -> bool:
return False return False
def acquire_lock(script_hint: str = "") -> bool: def acquire_lock() -> bool:
global _lock_file_path global _lock_file_path
ensure_dirs() ensure_dirs()
for f in list(APP_DIR.glob("*.lock")): for f in list(APP_DIR.glob("*.lock")):
@@ -84,7 +84,7 @@ def acquire_lock(script_hint: str = "") -> bool:
pass pass
is_running = False is_running = False
try: try:
is_running = _same_process(meta, psutil.Process(pid), script_hint) is_running = _same_process(meta, psutil.Process(pid))
except Exception: except Exception:
pass pass
if is_running: if is_running:
+41 -1
View File
@@ -56,6 +56,39 @@ from ui.ctk_theme import (
_tray_icon: Optional[object] = None _tray_icon: Optional[object] = None
_config: dict = {} _config: dict = {}
_exiting = False _exiting = False
_win_mutex_handle = None
_ERROR_ALREADY_EXISTS = 183
def _acquire_win_mutex() -> bool | None:
global _win_mutex_handle
try:
kernel32 = ctypes.windll.kernel32
kernel32.CreateMutexW.restype = ctypes.c_void_p
kernel32.CreateMutexW.argtypes = [ctypes.c_void_p, ctypes.c_bool, ctypes.c_wchar_p]
handle = kernel32.CreateMutexW(None, True, "Local\\TgWsProxy_SingleInstance")
if kernel32.GetLastError() == _ERROR_ALREADY_EXISTS:
kernel32.CloseHandle(ctypes.c_void_p(handle))
return False
if not handle:
return None
_win_mutex_handle = handle
return True
except Exception:
return None
def _release_win_mutex() -> None:
global _win_mutex_handle
if _win_mutex_handle:
try:
kernel32 = ctypes.windll.kernel32
kernel32.ReleaseMutex(ctypes.c_void_p(_win_mutex_handle))
kernel32.CloseHandle(ctypes.c_void_p(_win_mutex_handle))
except Exception:
pass
_win_mutex_handle = None
ICON_PATH = str(Path(__file__).parent / "icon.ico") ICON_PATH = str(Path(__file__).parent / "icon.ico")
@@ -350,13 +383,20 @@ def run_tray() -> None:
def main() -> None: def main() -> None:
if not acquire_lock("windows.py"): if mutex_result := _acquire_win_mutex() is False:
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0])) _show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return return
if mutex_result is None:
log.warning("Named mutex unavailable, falling back to lock file")
if not acquire_lock():
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return
try: try:
run_tray() run_tray()
finally: finally:
release_lock() release_lock()
_release_win_mutex()
if __name__ == "__main__": if __name__ == "__main__":