Skip to content

Commit fb15572

Browse files
Add files via upload
0 parents  commit fb15572

46 files changed

Lines changed: 7110 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# CONTRIBUTING
2+
3+
Спасибо за желание помочь проекту `tg-ws-proxy`.
4+
5+
## Перед созданием issue
6+
7+
1. Проверьте документацию в `docs/README.md`.
8+
2. Убедитесь, что похожий issue еще не открыт.
9+
3. Для корректной работы triage используйте стандартные лейблы из `.github/labels.md`.
10+
11+
## Как сообщать о проблемах
12+
13+
- Используйте шаблон `Проблема`.
14+
- По возможности укажите:
15+
- версию приложения,
16+
- ОС,
17+
- шаги воспроизведения,
18+
- ожидаемое и фактическое поведение,
19+
- лог-файл или текст ошибки.
20+
21+
Чем точнее описание, тем быстрее можно помочь.
22+
23+
## Локальный запуск из исходников
24+
25+
Требуется Python `>=3.8`.
26+
27+
```bash
28+
pip install -e .
29+
```
30+
31+
Запуск:
32+
33+
- консольный режим: `tg-ws-proxy`
34+
- Windows tray: `tg-ws-proxy-tray-win`
35+
- macOS tray: `tg-ws-proxy-tray-macos`
36+
- Linux tray: `tg-ws-proxy-tray-linux`
37+
38+
Подробности: `docs/BuildFromSource.md`.
39+
40+
## Pull Request
41+
42+
Перед открытием PR:
43+
44+
1. Убедитесь, что изменение решает конкретную проблему.
45+
2. Проверьте, что не сломаны существующие сценарии.
46+
3. Обновите документацию, если меняется поведение или настройка.
47+
48+
Небольшие и сфокусированные PR проверяются и принимаются быстрее.

Dockerfile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
FROM python:3.12-slim AS builder
4+
5+
ENV PYTHONDONTWRITEBYTECODE=1 \
6+
PYTHONUNBUFFERED=1 \
7+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
8+
PIP_NO_CACHE_DIR=1 \
9+
VIRTUAL_ENV=/opt/venv
10+
11+
RUN apt-get update \
12+
&& apt-get install -y --no-install-recommends build-essential cargo libffi-dev libssl-dev \
13+
&& python -m venv "$VIRTUAL_ENV" \
14+
&& "$VIRTUAL_ENV/bin/pip" install --upgrade pip setuptools wheel \
15+
&& rm -rf /var/lib/apt/lists/*
16+
17+
WORKDIR /app
18+
RUN "$VIRTUAL_ENV/bin/pip" install cryptography==46.0.5
19+
20+
FROM python:3.12-slim AS runtime
21+
22+
ENV PYTHONDONTWRITEBYTECODE=1 \
23+
PYTHONUNBUFFERED=1 \
24+
PATH=/opt/venv/bin:$PATH \
25+
TG_WS_PROXY_HOST=0.0.0.0 \
26+
TG_WS_PROXY_PORT=1443 \
27+
TG_WS_PROXY_SECRET="" \
28+
TG_WS_PROXY_DC_IPS="2:149.154.167.220 4:149.154.167.220"
29+
30+
RUN apt-get update \
31+
&& apt-get install -y --no-install-recommends tini ca-certificates \
32+
&& rm -rf /var/lib/apt/lists/* \
33+
&& groupadd --system app \
34+
&& useradd --system --gid app --create-home --home-dir /home/app app
35+
36+
WORKDIR /app
37+
COPY --from=builder /opt/venv /opt/venv
38+
COPY proxy ./proxy
39+
COPY docs/README.md LICENSE ./
40+
41+
USER app
42+
43+
EXPOSE 1443/tcp
44+
45+
ENTRYPOINT ["/usr/bin/tini", "--", "/bin/sh", "-lc", "set -eu; args=\"--host ${TG_WS_PROXY_HOST} --port ${TG_WS_PROXY_PORT}\"; for dc in ${TG_WS_PROXY_DC_IPS}; do args=\"$args --dc-ip $dc\"; done; if [ -n \"${TG_WS_PROXY_SECRET}\" ]; then args=\"$args --secret ${TG_WS_PROXY_SECRET}\"; fi; exec /opt/venv/bin/python -u proxy/tg_ws_proxy.py $args \"$@\"", "--"]
46+
CMD []

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Flowseal
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

docs/BuildFromSource.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Установка из исходников
2+
3+
## Консольный прокси
4+
5+
Для запуска только прокси без интерфейса системного трея достаточно базовой установки:
6+
7+
```bash
8+
pip install -e .
9+
tg-ws-proxy
10+
```
11+
12+
## Tray-приложение по ОС
13+
14+
### Windows 7/10+
15+
16+
```bash
17+
pip install -e .
18+
tg-ws-proxy-tray-win
19+
```
20+
21+
### macOS
22+
23+
```bash
24+
pip install -e .
25+
tg-ws-proxy-tray-macos
26+
```
27+
28+
### Linux
29+
30+
```bash
31+
pip install -e .
32+
tg-ws-proxy-tray-linux
33+
```
34+
35+
## Консольный режим из исходников
36+
37+
```bash
38+
tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v]
39+
```
40+
41+
**Аргументы:**
42+
43+
| Аргумент | По умолчанию | Описание |
44+
|---|---|---|
45+
| `--port` | `1443` | Порт прокси |
46+
| `--host` | `127.0.0.1` | Хост прокси |
47+
| `--secret` | `random` | 32-значный hex-ключ для авторизации клиентов |
48+
| `--dc-ip` | `2:149.154.167.220`, `4:149.154.167.220` | Целевой IP для DC (параметр можно указывать несколько раз) |
49+
| `--no-cfproxy` | `false` | Отключить попытку [проксирования через Cloudflare](./CfProxy.md) |
50+
| `--cfproxy-domain` | | Указать свой домен для проксирования через Cloudflare [Подробнее](./CfProxy.md). Можно указать несколько через повторение аргумента. |
51+
| `--cfproxy-worker-domain` | | Домен Cloudflare Worker [Подробнее](./CfWorker.md). Можно указать несколько через повторение аргумента. |
52+
| `--fake-tls-domain` | | Включить маскировку Fake TLS (ee-secret) с указанным SNI-доменом |
53+
| `--proxy-protocol` | выкл. | Принимать HAProxy PROXY protocol v1 (для работы за nginx/haproxy с `proxy_protocol on`) |
54+
| `--buf-kb` | `256` | Размер буфера в КБ |
55+
| `--pool-size` | `4` | Количество заготовленных соединений на каждый DC |
56+
| `--log-file` | выкл. | Путь к файлу, в который будут сохраняться логи |
57+
| `--log-max-mb` | `5` | Максимальный размер файла логов в МБ (после этого начинается перезапись) |
58+
| `--log-backups` | `0` | Количество сохранений логов после перезаписи |
59+
| `-v`, `--verbose` | выкл. | Подробное логирование (DEBUG) |
60+
61+
**Примеры:**
62+
63+
```bash
64+
# Стандартный запуск
65+
tg-ws-proxy
66+
67+
# Другой порт и дополнительные DC
68+
tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220
69+
70+
# С подробным логированием
71+
tg-ws-proxy -v
72+
73+
# Fake TLS маскировка (ee-secret)
74+
tg-ws-proxy --fake-tls-domain example.com
75+
```

docs/CfProxy.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Cloudflare-прокси
2+
3+
Для недоступных дата-центров можно использовать альтернативный бесплатный способ подключения — проксирование через Cloudflare. **Для работы нужен только домен**. В приложении есть домен по умолчанию, но его можно (и желательно) заменить на свой.
4+
5+
Прокси возвращает доступ к тому, что раньше не загружалось (реакции, некоторые стикеры). Если на аккаунте без Premium не загружаются фото/видео, оставьте в блоке `DC → IP` только `4:149.154.167.220`. Если CF-прокси работает, медиа снова начнет загружаться.
6+
7+
## Зачем мне настраивать свой домен?
8+
9+
Cloudflare имеет лимиты на одновременное количество WS-подключений. Домен по умолчанию может перестать работать в любой момент.
10+
11+
## Настройка своего домена
12+
13+
1. Добавьте свой домен в Cloudflare (либо купив его напрямую у Cloudflare, либо изменив NS-серверы: https://developers.cloudflare.com/dns/zone-setups/full-setup/setup/). Домены стоят примерно 150 рублей в год, подойдёт любой.
14+
15+
2. В `SSL/TLS``Overview` выставьте режим **Flexible**.
16+
17+
3. В `DNS``Records` добавьте следующие `A`-записи через `+ Add Record`:
18+
- Name=`kws1` IPv4=`149.154.175.50`
19+
- Name=`kws2` IPv4=`149.154.167.51`
20+
- Name=`kws3` IPv4=`149.154.175.100`
21+
- Name=`kws4` IPv4=`149.154.167.91`
22+
- Name=`kws5` IPv4=`149.154.171.5`
23+
- Name=`kws203` IPv4=`91.105.192.100`
24+
25+
4. **Добавьте домен в [zapret](https://github.com/Flowseal/zapret-discord-youtube/) или в любое другое ПО, так как подсеть Cloudflare может быть заблокирована (например, в России).**
26+
27+
5. В настройках `TgWsProxy` замените домен на свой.
28+
29+
## Благодарности
30+
31+
- Идея: https://github.com/Nekogram/WSProxy
32+
- Спасибо [@UjuiUjuMandan](https://github.com/UjuiUjuMandan) за информацию.

docs/CfWorker.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Cloudflare Worker
2+
3+
Альтернативный (полностью бесплатный, не нужно покупать домен в отличии от [CfProxy](./CfProxy.md)) способ проксирования.
4+
5+
Прокси возвращает доступ к тому, что раньше не загружалось (реакции, некоторые стикеры). Если на аккаунте без Premium с данным способом все еще не загружаются фото/видео, оставьте в блоке `DC → IP` только `4:149.154.167.220`
6+
7+
##
8+
9+
1. **Добавьте в [zapret](https://github.com/Flowseal/zapret-discord-youtube/) или в любое другое ПО следующие домены:**
10+
```
11+
cloudflare.com
12+
cloudflare.dev
13+
workers.dev
14+
```
15+
2. Создайте аккаунт в [Cloudflare](https://dash.cloudflare.com/) (или войдите в существующий)
16+
* **После создания аккаунта подтвердите почту с помощью письма, который вам пришел на email**
17+
3. Слева в панели выберите `Compute``Workers & Pages`
18+
<img width="250" height="768" alt="image" src="https://github.com/user-attachments/assets/d81e3522-045a-4e65-9c2e-5545b7ad409a" />
19+
20+
4. Нажмите сверху справа кнопку **`Create application`**`Start with Hello World!``Deploy`
21+
<img width="1406" height="193" alt="image" src="https://github.com/user-attachments/assets/7ac65944-8761-42a6-ab6d-ba5f9080c883" />
22+
<img width="586" height="379" alt="image" src="https://github.com/user-attachments/assets/ff901439-c2a1-4867-95de-e11b82a37044" />
23+
<img width="624" height="694" alt="image" src="https://github.com/user-attachments/assets/bb68d49a-166d-42a0-8fe2-bd2b16c0d066" />
24+
25+
5. Сверху справа нажмите кнопку **`Edit code`**, замените код слева на тот, [что находится внизу этой страницы](./CfWorker.md#код-workerа)
26+
* Если у вас не загружается код, то вы не выполнили первый пункт
27+
<img width="911" height="117" alt="image" src="https://github.com/user-attachments/assets/6bcdf839-d776-47e9-9d18-ba0efdf53244" />
28+
<img width="1027" height="512" alt="image" src="https://github.com/user-attachments/assets/daf131ed-82d5-40f0-a7eb-daeb598bea40" />
29+
30+
31+
6. Нажмите сверху справа кнопку **`Deploy`**
32+
<img width="415" height="138" alt="image" src="https://github.com/user-attachments/assets/58d8f83e-d8b5-40cf-a30f-741d7311047b" />
33+
34+
7. Скопируйте домен из поля справа и укажите его в настройках **Cloudflare Worker** (или через аргумент `--cfproxy-worker-domain`)
35+
* Пример домена: `random-symbols-1234.username.workers.dev`
36+
<img width="414" height="182" alt="image" src="https://github.com/user-attachments/assets/4fb0b111-8026-4d17-b993-6c70ec37f1f5" />
37+
38+
39+
### Код Worker'а
40+
```javascript
41+
import { connect } from "cloudflare:sockets";
42+
43+
function toBytes(data) {
44+
if (data instanceof ArrayBuffer) {
45+
return new Uint8Array(data);
46+
}
47+
if (typeof data === "string") {
48+
return new TextEncoder().encode(data);
49+
}
50+
if (data && typeof data.arrayBuffer === "function") {
51+
return data.arrayBuffer().then((ab) => new Uint8Array(ab));
52+
}
53+
return new Uint8Array();
54+
}
55+
56+
export default {
57+
async fetch(request) {
58+
if ((request.headers.get("Upgrade") || "").toLowerCase() !== "websocket") {
59+
return new Response("Expected websocket", { status: 426 });
60+
}
61+
62+
const url = new URL(request.url);
63+
if (url.pathname !== "/apiws") {
64+
return new Response("Not found", { status: 404 });
65+
}
66+
67+
const dst = url.searchParams.get("dst");
68+
const pair = new WebSocketPair();
69+
const client = pair[0];
70+
const server = pair[1];
71+
server.accept();
72+
73+
const socket = connect({ hostname: dst, port: 443 });
74+
const tcpReader = socket.readable.getReader();
75+
const tcpWriter = socket.writable.getWriter();
76+
77+
server.addEventListener("message", async (event) => {
78+
try {
79+
await tcpWriter.write(await toBytes(event.data));
80+
} catch {
81+
try {
82+
server.close(1011, "tcp write failed");
83+
} catch {}
84+
}
85+
});
86+
87+
server.addEventListener("close", async () => {
88+
try {
89+
await tcpWriter.close();
90+
} catch {}
91+
try {
92+
socket.close();
93+
} catch {}
94+
});
95+
96+
(async () => {
97+
try {
98+
while (true) {
99+
const { value, done } = await tcpReader.read();
100+
if (done) {
101+
break;
102+
}
103+
if (value) {
104+
server.send(value);
105+
}
106+
}
107+
} catch {
108+
} finally {
109+
try {
110+
server.close();
111+
} catch {}
112+
try {
113+
tcpReader.releaseLock();
114+
} catch {}
115+
try {
116+
socket.close();
117+
} catch {}
118+
}
119+
})();
120+
121+
return new Response(null, { status: 101, webSocket: client });
122+
},
123+
};
124+
```

0 commit comments

Comments
 (0)