[FEAT] добавление поддержки Linux (GNOME): - Кроссплатформенная работа с буфером обмена

- Открытие файлов через xdg-open для Linux
      - Поддержка tg:// протокола в Linux
      - .desktop файл для интеграции в окружение GNOME
      - Скрипты установки и удаления
      - Обновлённая документация
This commit is contained in:
Goga-Rid 2026-03-06 07:06:44 +03:00
parent f8a10d9940
commit 155bf5965e
5 changed files with 365 additions and 14 deletions

View File

@ -20,7 +20,37 @@ Telegram Desktop → SOCKS5 (127.0.0.1:1080) → TG WS Proxy → WSS (kws*.web.t
## Установка ## Установка
### Из исходников ### Fedora (GNOME)
Для установки в Linux с GNOME выполните:
### Ubuntu/Debian:
```bash
# Установите зависимости
sudo apt install libappindicator3-1 python3-tk xclip
# Запустите скрипт установки
./install.sh
```
### Fedora / RHEL:
```bash
# Установите зависимости
sudo dnf install python3-pip python3-tkinter libappindicator-gtk3
# Запустите скрипт установки
./install.sh
```
После установки приложение появится в меню приложений и будет доступно по команде `tg-ws-proxy`.
**Для удаления:**
```bash
./uninstall.sh
```
### Из исходников (кроссплатформенно)
```bash ```bash
pip install -r requirements.txt pip install -r requirements.txt
@ -28,7 +58,7 @@ pip install -r requirements.txt
## Использование ## Использование
### Tray-приложение (рекомендуется для Windows) ### Tray-приложение (Linux/Windows)
```bash ```bash
python tg_ws_tray.py python tg_ws_tray.py
@ -87,7 +117,11 @@ python tg_ws_proxy.py -v
## Конфигурация ## Конфигурация
Tray-приложение хранит конфигурацию в `%APPDATA%/TgWsProxy/config.json`: Tray-приложение хранит конфигурацию в:
- **Windows:** `%APPDATA%/TgWsProxy/config.json`
- **Linux:** `~/.config/tgwsproxy/config.json`
Пример конфигурации:
```json ```json
{ {
@ -100,7 +134,9 @@ Tray-приложение хранит конфигурацию в `%APPDATA%/Tg
} }
``` ```
Логи записываются в `%APPDATA%/TgWsProxy/proxy.log`. Логи записываются в:
- **Windows:** `%APPDATA%/TgWsProxy/proxy.log`
- **Linux:** `~/.config/tgwsproxy/proxy.log`
## Сборка exe ## Сборка exe
@ -111,6 +147,14 @@ pip install pyinstaller
pyinstaller tg_ws_proxy.spec pyinstaller tg_ws_proxy.spec
``` ```
## Требования для Linux
Для работы tray-приложения в GNOME требуется:
```bash
sudo dnf install libappindicator-gtk3 python3-tkinter xclip
```
## Дисклеймер ## Дисклеймер
Проект частично vibecoded by Opus 4.6. Если вы найдете баг, то создайте Issue с его описанем. Проект частично vibecoded by Opus 4.6. Если вы найдете баг, то создайте Issue с его описанем.

116
install.sh Executable file
View File

@ -0,0 +1,116 @@
#!/bin/bash
# Установка TG WS Proxy в Fedora (GNOME)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_NAME="tg-ws-proxy"
APP_DIR="$HOME/.local/share/$APP_NAME"
BIN_DIR="$HOME/.local/bin"
DESKTOP_DIR="$HOME/.local/share/applications"
ICON_DIR="$HOME/.local/share/icons/hicolor"
echo "🔧 Установка TG WS Proxy..."
# Создаём директории
mkdir -p "$APP_DIR"
mkdir -p "$BIN_DIR"
mkdir -p "$DESKTOP_DIR"
mkdir -p "$ICON_DIR/scalable/apps"
mkdir -p "$ICON_DIR/512x512/apps"
mkdir -p "$ICON_DIR/256x256/apps"
mkdir -p "$ICON_DIR/128x128/apps"
mkdir -p "$ICON_DIR/64x64/apps"
mkdir -p "$ICON_DIR/32x32/apps"
# Копируем файлы приложения
echo "📁 Копирование файлов..."
cp "$SCRIPT_DIR/tg_ws_proxy.py" "$APP_DIR/"
cp "$SCRIPT_DIR/tg_ws_tray.py" "$APP_DIR/"
cp "$SCRIPT_DIR/icon.ico" "$APP_DIR/" 2>/dev/null || true
# Создаём скрипт запуска
echo "📝 Создание скрипта запуска..."
cat > "$BIN_DIR/tg-ws-proxy" << 'EOF'
#!/bin/bash
exec python3 "$HOME/.local/share/tg-ws-proxy/tg_ws_tray.py" "$@"
EOF
chmod +x "$BIN_DIR/tg-ws-proxy"
# Создаём .desktop файл
echo "🖥️ Создание .desktop файла..."
cat > "$DESKTOP_DIR/tg-ws-proxy.desktop" << EOF
[Desktop Entry]
Version=1.0
Name=TG WS Proxy
Name[ru]=TG WS Proxy
Comment=Telegram WebSocket Proxy for Desktop
Comment[ru]=Telegram WebSocket Proxy для рабочего стола
Exec=$BIN_DIR/tg-ws-proxy
Icon=tg-ws-proxy
Terminal=false
Type=Application
Categories=Network;Proxy;
Keywords=telegram;proxy;socks;
StartupNotify=false
EOF
# Создаём иконки из icon.ico или генерируем SVG
echo "🎨 Установка иконок..."
# Создаём SVG иконку (универсальная)
cat > "$ICON_DIR/scalable/apps/tg-ws-proxy.svg" << 'EOF'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<circle cx="64" cy="64" r="60" fill="#3390ec"/>
<text x="64" y="88" font-family="Arial, sans-serif" font-size="72" font-weight="bold" fill="white" text-anchor="middle">T</text>
</svg>
EOF
# Генерируем PNG иконки из SVG с помощью convert (если есть) или создаём заглушки
if command -v convert &> /dev/null; then
for size in 512 256 128 64 32; do
convert -background none \
"$ICON_DIR/scalable/apps/tg-ws-proxy.svg" \
-resize "${size}x${size}" \
"$ICON_DIR/${size}x${size}/apps/tg-ws-proxy.png" 2>/dev/null || true
done
fi
# Если PNG не создались, создадим символические ссылки на SVG
for size in 512 256 128 64 32; do
if [ ! -f "$ICON_DIR/${size}x${size}/apps/tg-ws-proxy.png" ]; then
ln -sf ../../scalable/apps/tg-ws-proxy.svg \
"$ICON_DIR/${size}x${size}/apps/tg-ws-proxy.png" 2>/dev/null || true
fi
done
# Обновляем кэш иконок
if command -v gtk-update-icon-cache &> /dev/null; then
gtk-update-icon-cache -f "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
fi
# Обновляем базу данных desktop файлов
if command -v update-desktop-database &> /dev/null; then
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
fi
# Устанавливаем зависимости Python
echo "📦 Установка Python зависимостей..."
if command -v pip3 &> /dev/null; then
pip3 install --user -r "$SCRIPT_DIR/requirements.txt"
elif command -v pip &> /dev/null; then
pip install --user -r "$SCRIPT_DIR/requirements.txt"
else
echo "⚠️ pip не найден. Установите зависимости вручную:"
echo " pip install -r requirements.txt"
fi
echo ""
echo "✅ Установка завершена!"
echo ""
echo "📍 Приложение установлено в: $APP_DIR"
echo "🚀 Запустить можно через меню приложений или командой: tg-ws-proxy"
echo ""
echo "⚠️ Для работы в GNOME может потребзоваться AppIndicator:"
echo " sudo dnf install libappindicator-gtk3"
echo ""

13
tg-ws-proxy.desktop Normal file
View File

@ -0,0 +1,13 @@
[Desktop Entry]
Version=1.0
Name=TG WS Proxy
Name[ru]=TG WS Proxy
Comment=Telegram WebSocket Proxy for Desktop
Comment[ru]=Telegram WebSocket Proxy для рабочего стола
Exec=python3 %h/tg_ws_tray.py
Icon=tg-ws-proxy
Terminal=false
Type=Application
Categories=Network;Proxy;
Keywords=telegram;proxy;socks;
StartupNotify=false

View File

@ -1,10 +1,10 @@
from __future__ import annotations from __future__ import annotations
import ctypes
import json import json
import logging import logging
import os import os
import psutil import platform
import subprocess
import sys import sys
import threading import threading
import time import time
@ -25,15 +25,33 @@ except ImportError:
try: try:
import customtkinter as ctk import customtkinter as ctk
from tkinter import messagebox, filedialog
except ImportError: except ImportError:
ctk = None # type: ignore ctk = None # type: ignore
messagebox = None # type: ignore
filedialog = None # type: ignore
try:
import psutil
except ImportError:
psutil = None # type: ignore
# Proxy engine # Proxy engine
import tg_ws_proxy import tg_ws_proxy
APP_NAME = "TgWsProxy" APP_NAME = "TgWsProxy"
APP_DIR = Path(os.environ.get("APPDATA", Path.home())) / APP_NAME IS_WINDOWS = platform.system() == "Windows"
IS_LINUX = platform.system() == "Linux"
# Platform-specific paths
if IS_WINDOWS:
APP_DIR = Path(os.environ.get("APPDATA", Path.home())) / APP_NAME
elif IS_LINUX:
APP_DIR = Path.home() / ".config" / APP_NAME.lower()
else:
APP_DIR = Path.home() / f".{APP_NAME.lower()}"
CONFIG_FILE = APP_DIR / "config.json" CONFIG_FILE = APP_DIR / "config.json"
LOG_FILE = APP_DIR / "proxy.log" LOG_FILE = APP_DIR / "proxy.log"
FIRST_RUN_MARKER = APP_DIR / ".first_run_done" FIRST_RUN_MARKER = APP_DIR / ".first_run_done"
@ -212,11 +230,104 @@ def restart_proxy():
def _show_error(text: str, title: str = "TG WS Proxy — Ошибка"): def _show_error(text: str, title: str = "TG WS Proxy — Ошибка"):
ctypes.windll.user32.MessageBoxW(0, text, title, 0x10) """Показать диалог ошибки (кроссплатформенно)."""
if IS_WINDOWS:
try:
import ctypes
ctypes.windll.user32.MessageBoxW(0, text, title, 0x10)
return
except Exception:
pass
# Linux / fallback
if messagebox:
root = ctk.CTk() if ctk else None
if root:
root.withdraw()
messagebox.showerror(title, text, parent=root)
root.destroy()
else:
log.error("%s: %s", title, text)
else:
log.error("%s: %s", title, text)
def _show_info(text: str, title: str = "TG WS Proxy"): def _show_info(text: str, title: str = "TG WS Proxy"):
ctypes.windll.user32.MessageBoxW(0, text, title, 0x40) """Показать информационный диалог (кроссплатформенно)."""
if IS_WINDOWS:
try:
import ctypes
ctypes.windll.user32.MessageBoxW(0, text, title, 0x40)
return
except Exception:
pass
# Linux / fallback
if messagebox:
root = ctk.CTk() if ctk else None
if root:
root.withdraw()
messagebox.showinfo(title, text, parent=root)
root.destroy()
else:
log.info("%s: %s", title, text)
else:
log.info("%s: %s", title, text)
def _copy_to_clipboard(text: str):
"""Copy text to clipboard (кроссплатформенно)."""
if IS_WINDOWS:
try:
import ctypes.wintypes
CF_UNICODETEXT = 13
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
user32.OpenClipboard(0)
user32.EmptyClipboard()
encoded = text.encode("utf-16-le") + b"\x00\x00"
h = kernel32.GlobalAlloc(0x0042, len(encoded))
p = kernel32.GlobalLock(h)
ctypes.memmove(p, encoded, len(encoded))
kernel32.GlobalUnlock(h)
user32.SetClipboardData(CF_UNICODETEXT, h)
user32.CloseClipboard()
return
except Exception as exc:
log.error("Windows clipboard failed: %s", exc)
# Linux: try xclip or xsel
if IS_LINUX:
for cmd in [["xclip", "-selection", "clipboard"],
["xsel", "--clipboard", "--input"]]:
try:
proc = subprocess.Popen(
cmd, stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
proc.communicate(text.encode("utf-8"))
if proc.returncode == 0:
log.debug("Copied via %s", cmd[0])
return
except Exception:
continue
log.warning("xclip/xsel not available for clipboard")
# Fallback: try tkinter clipboard
try:
import tkinter as tk
root = tk.Tk()
root.withdraw()
root.clipboard_clear()
root.clipboard_append(text)
root.update()
root.destroy()
log.debug("Copied via tkinter clipboard")
return
except Exception:
pass
log.error("Failed to copy to clipboard on this platform")
def _on_open_in_telegram(icon=None, item=None): def _on_open_in_telegram(icon=None, item=None):
@ -393,10 +504,26 @@ def _edit_config_dialog():
root.mainloop() root.mainloop()
def _open_file_platform_specific(path: Path):
"""Открыть файл в стандартном приложении платформы."""
try:
if IS_WINDOWS:
os.startfile(str(path))
elif IS_LINUX:
# для xdg-open (freedesktop standard)
subprocess.Popen(["xdg-open", str(path)])
else:
# для macOS
subprocess.Popen(["open", str(path)])
except Exception as exc:
log.error("Failed to open file: %s", exc)
_show_error(f"Не удалось открыть файл:\n{exc}")
def _on_open_logs(icon=None, item=None): def _on_open_logs(icon=None, item=None):
log.info("Opening log file: %s", LOG_FILE) log.info("Opening log file: %s", LOG_FILE)
if LOG_FILE.exists(): if LOG_FILE.exists():
os.startfile(str(LOG_FILE)) _open_file_platform_specific(LOG_FILE)
else: else:
_show_info("Файл логов ещё не создан.", "TG WS Proxy") _show_info("Файл логов ещё не создан.", "TG WS Proxy")
@ -589,9 +716,10 @@ def main():
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0])) _show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return return
# Hide console window if running as frozen exe # Hide console window if running as frozen exe on Windows
if getattr(sys, "frozen", False): if getattr(sys, "frozen", False) and IS_WINDOWS:
try: try:
import ctypes
ctypes.windll.user32.ShowWindow( ctypes.windll.user32.ShowWindow(
ctypes.windll.kernel32.GetConsoleWindow(), 0) ctypes.windll.kernel32.GetConsoleWindow(), 0)
except Exception: except Exception:

50
uninstall.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
# Удаление TG WS Proxy из Fedora
set -e
APP_NAME="tg-ws-proxy"
APP_DIR="$HOME/.local/share/$APP_NAME"
BIN_DIR="$HOME/.local/bin"
DESKTOP_DIR="$HOME/.local/share/applications"
ICON_DIR="$HOME/.local/share/icons/hicolor"
echo "🗑️ Удаление TG WS Proxy..."
# Удаляем файлы приложения
rm -rf "$APP_DIR"
# Удаляем скрипт запуска
rm -f "$BIN_DIR/tg-ws-proxy"
# Удаляем .desktop файл
rm -f "$DESKTOP_DIR/tg-ws-proxy.desktop"
# Удаляем иконки
rm -f "$ICON_DIR/scalable/apps/tg-ws-proxy.svg"
for size in 512 256 128 64 32; do
rm -f "$ICON_DIR/${size}x${size}/apps/tg-ws-proxy.png"
done
# Обновляем кэш иконок
if command -v gtk-update-icon-cache &> /dev/null; then
gtk-update-icon-cache -f "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
fi
# Обновляем базу данных desktop файлов
if command -v update-desktop-database &> /dev/null; then
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
fi
# Удаляем конфиг и логи (опционально)
if [ -d "$HOME/.config/tgwsproxy" ]; then
read -p "📁 Удалить конфиг и логи? (~/.config/tgwsproxy) [y/N]: " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -rf "$HOME/.config/tgwsproxy"
echo "🗑️ Конфиг и логи удалены"
fi
fi
echo ""
echo "✅ TG WS Proxy удалён!"