@emaxe/tuigram 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -27,3 +27,20 @@ AUTO_SCROLL=true
27
27
 
28
28
  # Показывать уведомления о наборе текста (true/false)
29
29
  SHOW_TYPING=true
30
+
31
+ # ── Proxy Settings ────────────────────────────────────────────────────────────
32
+ # Поддерживаются HTTP и SOCKS5 (как с авторизацией, так и без).
33
+ # Формат единого URL:
34
+ # PROXY_URL=http://127.0.0.1:8080
35
+ # PROXY_URL=http://user:password@proxy.example.com:8080
36
+ # PROXY_URL=socks5://127.0.0.1:1080
37
+ # PROXY_URL=socks5://user:password@127.0.0.1:1080
38
+ #
39
+ # Либо отдельными переменными:
40
+ # PROXY_TYPE=http
41
+ # PROXY_HOST=127.0.0.1
42
+ # PROXY_PORT=8080
43
+ # PROXY_USERNAME=
44
+ # PROXY_PASSWORD=
45
+ # PROXY_TIMEOUT=10
46
+
package/CHANGELOG.md CHANGED
@@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
13
13
 
14
14
  - English localization of the TUI and CLI interface strings.
15
15
 
16
+ ## [1.1.0] — 2026-08-31
17
+
18
+ ### Added
19
+
20
+ - Support for HTTP/HTTPS (via HTTP CONNECT) and SOCKS5/SOCKS4 proxies with optional username and password authentication.
21
+ - Proxy configuration options via `.env` (`PROXY_URL` or `PROXY_TYPE`/`PROXY_HOST`/`PROXY_PORT`/`PROXY_USERNAME`/`PROXY_PASSWORD`) and standard environment variables (`HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`).
22
+ - Display of active proxy status with password masking in `tuigram paths`.
23
+
16
24
  ## [1.0.1] — 2026-08-29
17
25
 
18
26
  Documentation and packaging only — no runtime changes.
@@ -103,6 +111,7 @@ First public release.
103
111
  - A WCAG contrast test (3:1 threshold) applied to every theme **after** conversion
104
112
  to xterm-256 — the colors are checked exactly as the user sees them.
105
113
 
106
- [Unreleased]: https://github.com/emaxe/tuigram/compare/v1.0.1...HEAD
114
+ [Unreleased]: https://github.com/emaxe/tuigram/compare/v1.1.0...HEAD
115
+ [1.1.0]: https://github.com/emaxe/tuigram/compare/v1.0.1...v1.1.0
107
116
  [1.0.1]: https://github.com/emaxe/tuigram/compare/v1.0.0...v1.0.1
108
117
  [1.0.0]: https://github.com/emaxe/tuigram/releases/tag/v1.0.0
package/CHANGELOG.ru.md CHANGED
@@ -13,6 +13,14 @@
13
13
 
14
14
  - Английская локализация строк интерфейса TUI и CLI.
15
15
 
16
+ ## [1.1.0] — 2026-08-31
17
+
18
+ ### Добавлено
19
+
20
+ - Поддержка работы через HTTP/HTTPS (HTTP CONNECT) и SOCKS5/SOCKS4 прокси, как с авторизацией по логину и паролю, так и без неё.
21
+ - Настройка прокси через `.env` (`PROXY_URL` или `PROXY_TYPE`/`PROXY_HOST`/`PROXY_PORT`/`PROXY_USERNAME`/`PROXY_PASSWORD`) и стандартные переменные окружения (`HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`).
22
+ - Отображение активного прокси с маскированием пароля в выводе команды `tuigram paths`.
23
+
16
24
  ## [1.0.1] — 2026-08-29
17
25
 
18
26
  Только документация и упаковка — поведение клиента не менялось.
@@ -105,6 +113,7 @@
105
113
  - Тест контраста по WCAG (порог 3:1) для каждой темы — **после** конверсии в xterm-256,
106
114
  то есть ровно в том виде, в каком цвет увидит пользователь.
107
115
 
108
- [Unreleased]: https://github.com/emaxe/tuigram/compare/v1.0.1...HEAD
116
+ [Unreleased]: https://github.com/emaxe/tuigram/compare/v1.1.0...HEAD
117
+ [1.1.0]: https://github.com/emaxe/tuigram/compare/v1.0.1...v1.1.0
109
118
  [1.0.1]: https://github.com/emaxe/tuigram/compare/v1.0.0...v1.0.1
110
119
  [1.0.0]: https://github.com/emaxe/tuigram/releases/tag/v1.0.0
package/README.md CHANGED
@@ -55,6 +55,7 @@ TuiGram lets you use Telegram entirely from the terminal: browse your dialog lis
55
55
  - [Slash commands](#-slash-commands-in-the-input-box)
56
56
  - [Sending files and images](#-sending-files-and-images)
57
57
  - [Themes](#-themes)
58
+ - [Proxy configuration](#-proxy-configuration)
58
59
  - [Command line usage (CLI)](#️-command-line-usage-cli)
59
60
  - [Project structure](#-project-structure)
60
61
  - [Security](#-security)
@@ -511,6 +512,35 @@ The contrast of every "text on background" pair is verified by an automated WCAG
511
512
 
512
513
  ---
513
514
 
515
+ ## 🌐 Proxy configuration
516
+
517
+ TuiGram supports routing MTProto connections through HTTP (including HTTPS CONNECT) and SOCKS5/SOCKS4 proxies — both with and without username/password authentication.
518
+
519
+ Proxy options can be configured in `.env` (or via environment variables):
520
+
521
+ **Single URL:**
522
+ ```env
523
+ PROXY_URL=http://127.0.0.1:8080
524
+ PROXY_URL=http://user:password@proxy.example.com:8080
525
+ PROXY_URL=socks5://127.0.0.1:1080
526
+ PROXY_URL=socks5://user:password@127.0.0.1:1080
527
+ ```
528
+
529
+ **Or separate variables:**
530
+ ```env
531
+ PROXY_TYPE=http # http, https, socks5, socks4
532
+ PROXY_HOST=127.0.0.1
533
+ PROXY_PORT=8080
534
+ PROXY_USERNAME=user # optional
535
+ PROXY_PASSWORD=password # optional
536
+ PROXY_TIMEOUT=10 # timeout in seconds (default: 10)
537
+ ```
538
+
539
+ Standard environment variables `HTTPS_PROXY`, `HTTP_PROXY`, and `ALL_PROXY` are also supported as fallbacks.
540
+ The active proxy status can be inspected using `tuigram paths`.
541
+
542
+ ---
543
+
514
544
  ## 🛠️ Command line usage (CLI)
515
545
 
516
546
  TuiGram can be used as a set of console utilities (when running from a repository
@@ -554,6 +584,7 @@ TuiGram/
554
584
  │ ├── state.js # Reactive centralized state store
555
585
  │ ├── telegram/
556
586
  │ │ ├── client.js # MTProto client creation and management
587
+ │ │ ├── socket.js # MTProto network transport and proxy tunneling (HTTP/SOCKS5)
557
588
  │ │ ├── auth.js # Interactive login wizard and 2FA
558
589
  │ │ ├── dialogs.js # Fetching, filtering and searching dialogs
559
590
  │ │ ├── messages.js # History loading, sending, editing, files, reactions
package/README.ru.md CHANGED
@@ -51,6 +51,7 @@
51
51
  - [Слэш-команды](#-слэш-команды-в-поле-ввода)
52
52
  - [Отправка файлов и картинок](#-отправка-файлов-и-картинок)
53
53
  - [Темы оформления](#-темы-оформления)
54
+ - [Прокси-сервер](#-прокси-сервер)
54
55
  - [Использование через консоль (CLI)](#️-использование-через-консоль-cli)
55
56
  - [Структура проекта](#-структура-проекта)
56
57
  - [Безопасность](#-безопасность)
@@ -505,6 +506,35 @@ TUI_THEME=light # светлая
505
506
 
506
507
  ---
507
508
 
509
+ ## 🌐 Прокси-сервер
510
+
511
+ TuiGram поддерживает маршрутизацию MTProto-соединений через HTTP (включая HTTPS CONNECT) и SOCKS5/SOCKS4 прокси — как с авторизацией по логину и паролю, так и без неё.
512
+
513
+ Настройки прокси задаются в `.env` (или через переменные окружения):
514
+
515
+ **Единый URL:**
516
+ ```env
517
+ PROXY_URL=http://127.0.0.1:8080
518
+ PROXY_URL=http://user:password@proxy.example.com:8080
519
+ PROXY_URL=socks5://127.0.0.1:1080
520
+ PROXY_URL=socks5://user:password@127.0.0.1:1080
521
+ ```
522
+
523
+ **Либо отдельными переменными:**
524
+ ```env
525
+ PROXY_TYPE=http # http, https, socks5, socks4
526
+ PROXY_HOST=127.0.0.1
527
+ PROXY_PORT=8080
528
+ PROXY_USERNAME=user # опционально
529
+ PROXY_PASSWORD=password # опционально
530
+ PROXY_TIMEOUT=10 # таймаут в секундах (по умолчанию 10)
531
+ ```
532
+
533
+ Также в качестве фолбэка поддерживаются стандартные переменные `HTTPS_PROXY`, `HTTP_PROXY` и `ALL_PROXY`.
534
+ Проверить активное состояние прокси можно командой `tuigram paths`.
535
+
536
+ ---
537
+
508
538
  ## 🛠️ Использование через консоль (CLI)
509
539
 
510
540
  TuiGram можно запускать в режиме консольных утилит (при запуске из клона
@@ -548,6 +578,7 @@ TuiGram/
548
578
  │ ├── state.js # Реактивное централизованное хранилище состояния
549
579
  │ ├── telegram/
550
580
  │ │ ├── client.js # Создание и управление MTProto клиентом
581
+ │ │ ├── socket.js # Сетевой транспорт MTProto и туннелирование прокси (HTTP/SOCKS5)
551
582
  │ │ ├── auth.js # Интерактивный логин-визард и 2FA
552
583
  │ │ ├── dialogs.js # Получение, фильтрация и поиск диалогов
553
584
  │ │ ├── messages.js # Загрузка истории, отправка, правка, файлы, реакции
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emaxe/tuigram",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Полнофункциональный TUI & CLI клиент Telegram на Node.js на базе MTProto (teleproto)",
5
5
  "type": "module",
6
6
  "exports": {
package/src/cli/init.js CHANGED
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import input from "input";
4
4
  import { bold, cyan, green, yellow, dim } from "colorette";
5
- import { config } from "../config.js";
5
+ import { config, formatProxyUrl } from "../config.js";
6
6
 
7
7
  /** Сколько раз переспрашивать значение, прежде чем сдаться. */
8
8
  const MAX_ATTEMPTS = 3;
@@ -193,5 +193,6 @@ export function cmdPaths() {
193
193
  console.log(` Сессия: ${config.sessionPath} ${sessionExists ? green("(есть)") : dim("(нет)")}`);
194
194
  console.log(` Загрузки: ${config.downloadsDir}`);
195
195
  console.log(` Ключи API: ${config.apiId && config.apiHash ? green("заданы") : yellow("не заданы — запустите tuigram init")}`);
196
+ console.log(` Прокси: ${config.proxy ? green(formatProxyUrl(config.proxy)) : dim("не используется")}`);
196
197
  console.log("");
197
198
  }
package/src/config.js CHANGED
@@ -110,6 +110,129 @@ function readVersion() {
110
110
  }
111
111
  }
112
112
 
113
+ /**
114
+ * @typedef {Object} ProxyConfig
115
+ * @property {"http"|"https"|"socks5"|"socks4"} type - Протокол прокси
116
+ * @property {string} host - Адрес сервера прокси
117
+ * @property {number} port - Порт сервера прокси
118
+ * @property {string} [username] - Логин пользователя
119
+ * @property {string} [password] - Пароль пользователя
120
+ * @property {number} [timeout] - Таймаут в секундах
121
+ * @property {string} ip - Хост прокси (для совместимости с MTProto)
122
+ * @property {4|5} [socksType] - Тип SOCKS-прокси
123
+ * @property {boolean} [http] - Флаг HTTP-прокси
124
+ */
125
+
126
+ /**
127
+ * Нормализует тип прокси из строки протокола.
128
+ * @param {string} [raw]
129
+ * @returns {"http"|"https"|"socks5"|"socks4"}
130
+ */
131
+ function normalizeProxyType(raw) {
132
+ const clean = String(raw || "").toLowerCase().replace(/:$/, "").trim();
133
+ if (clean === "https") return "https";
134
+ if (clean === "socks4" || clean === "socks4a") return "socks4";
135
+ if (clean === "socks5" || clean === "socks5h" || clean === "socks") return "socks5";
136
+ return "http";
137
+ }
138
+
139
+ /**
140
+ * Разбирает параметры прокси из переменных окружения или переданного объекта.
141
+ * Поддерживает URL-формат (PROXY_URL, HTTPS_PROXY, HTTP_PROXY, ALL_PROXY)
142
+ * и отдельные переменные (PROXY_TYPE, PROXY_HOST, PROXY_PORT, PROXY_USERNAME, PROXY_PASSWORD).
143
+ * @param {Record<string, string|undefined>} [env=process.env]
144
+ * @returns {ProxyConfig|null}
145
+ */
146
+ export function parseProxyConfig(env = process.env) {
147
+ const rawUrl = env.PROXY_URL || env.proxy_url || (!env.PROXY_HOST && !env.PROXY_IP && !env.proxy_host && !env.proxy_ip
148
+ ? (env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy)
149
+ : undefined);
150
+
151
+ let type = "http";
152
+ let host = "";
153
+ let port = 0;
154
+ let username = "";
155
+ let password = "";
156
+
157
+ if (rawUrl && typeof rawUrl === "string" && rawUrl.trim()) {
158
+ let urlStr = rawUrl.trim();
159
+ // Добавляем схему по умолчанию, если передан host:port без префикса
160
+ if (!/^[a-zA-Z0-9+-.]+:\/\//.test(urlStr)) {
161
+ const defaultScheme = (env.PROXY_TYPE || "http").toLowerCase().replace(/:$/, "");
162
+ urlStr = `${defaultScheme}://${urlStr}`;
163
+ }
164
+
165
+ try {
166
+ const parsed = new URL(urlStr);
167
+ type = normalizeProxyType(parsed.protocol);
168
+ host = parsed.hostname.replace(/^\[(.*)\]$/, "$1");
169
+ port = parsed.port ? parseInt(parsed.port, 10) : (type === "https" ? 443 : (type.startsWith("socks") ? 1080 : 8080));
170
+ if (parsed.username) username = decodeURIComponent(parsed.username);
171
+ if (parsed.password) password = decodeURIComponent(parsed.password);
172
+ } catch {
173
+ return null;
174
+ }
175
+ } else if (env.PROXY_HOST || env.PROXY_IP) {
176
+ type = normalizeProxyType(env.PROXY_TYPE || "http");
177
+ host = String(env.PROXY_HOST || env.PROXY_IP).trim();
178
+ const defaultPort = type === "https" ? 443 : (type.startsWith("socks") ? 1080 : 8080);
179
+ port = env.PROXY_PORT ? parseInt(String(env.PROXY_PORT).trim(), 10) : defaultPort;
180
+ username = String(env.PROXY_USERNAME || env.PROXY_USER || "").trim();
181
+ password = String(env.PROXY_PASSWORD || env.PROXY_PASS || "").trim();
182
+ } else {
183
+ return null;
184
+ }
185
+
186
+ if (!host || !port || isNaN(port) || port <= 0 || port > 65535) {
187
+ return null;
188
+ }
189
+
190
+ const rawTimeout = env.PROXY_TIMEOUT ? parseInt(String(env.PROXY_TIMEOUT).trim(), 10) : 10;
191
+ const timeout = isNaN(rawTimeout) || rawTimeout <= 0 ? 10 : rawTimeout;
192
+
193
+ /** @type {ProxyConfig} */
194
+ const result = {
195
+ type,
196
+ host,
197
+ port,
198
+ ip: host,
199
+ timeout,
200
+ };
201
+
202
+ if (username) result.username = username;
203
+ if (password) result.password = password;
204
+
205
+ if (type === "socks5") {
206
+ result.socksType = 5;
207
+ } else if (type === "socks4") {
208
+ result.socksType = 4;
209
+ } else {
210
+ result.http = true;
211
+ }
212
+
213
+ return result;
214
+ }
215
+
216
+ /**
217
+ * Форматирует конфигурацию прокси в строку для вывода в терминал.
218
+ * Пароль маскируется звездочками в целях безопасности.
219
+ * @param {ProxyConfig|null} proxy
220
+ * @returns {string}
221
+ */
222
+ export function formatProxyUrl(proxy) {
223
+ if (!proxy) return "не используется";
224
+
225
+ let auth = "";
226
+ if (proxy.username) {
227
+ auth = proxy.password
228
+ ? `${encodeURIComponent(proxy.username)}:***@`
229
+ : `${encodeURIComponent(proxy.username)}@`;
230
+ }
231
+
232
+ const host = proxy.host.includes(":") ? `[${proxy.host}]` : proxy.host;
233
+ return `${proxy.type}://${auth}${host}:${proxy.port}`;
234
+ }
235
+
113
236
  export const config = {
114
237
  packageRoot,
115
238
  /** @deprecated оставлено для обратной совместимости, равно packageRoot */
@@ -129,6 +252,8 @@ export const config = {
129
252
  autoScroll: String(process.env.AUTO_SCROLL || "true").toLowerCase() === "true",
130
253
  showTyping: String(process.env.SHOW_TYPING || "true").toLowerCase() === "true",
131
254
 
255
+ proxy: parseProxyConfig(),
256
+
132
257
  /**
133
258
  * Путь к файлу сессии.
134
259
  * @returns {string}
@@ -4,6 +4,7 @@ import { StringSession } from "teleproto/sessions/index.js";
4
4
  import { Logger, LogLevel } from "teleproto/extensions/Logger.js";
5
5
  import { config } from "../config.js";
6
6
  import { saveSessionFile, readFileSafe } from "../utils/storage.js";
7
+ import { TuiGramNetSockets } from "./socket.js";
7
8
 
8
9
  /**
9
10
  * Читает сохранённую строку сессии.
@@ -42,17 +43,24 @@ export function clearSession() {
42
43
  export function buildClient(sessionString = readSession()) {
43
44
  config.assertCredentials();
44
45
 
46
+ const clientParams = {
47
+ connectionRetries: 10,
48
+ autoReconnect: true,
49
+ retryDelay: 1500,
50
+ baseLogger: new Logger(LogLevel.ERROR),
51
+ useWSS: false,
52
+ };
53
+
54
+ if (config.proxy) {
55
+ clientParams.proxy = config.proxy;
56
+ clientParams.networkSocket = TuiGramNetSockets;
57
+ }
58
+
45
59
  const client = new TelegramClient(
46
60
  new StringSession(sessionString),
47
61
  config.apiId,
48
62
  config.apiHash,
49
- {
50
- connectionRetries: 10,
51
- autoReconnect: true,
52
- retryDelay: 1500,
53
- baseLogger: new Logger(LogLevel.ERROR),
54
- useWSS: false,
55
- }
63
+ clientParams
56
64
  );
57
65
 
58
66
  return client;
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Сетевой транспорт для MTProto с поддержкой прокси (HTTP CONNECT, SOCKS5, SOCKS4).
3
+ */
4
+ import http from "node:http";
5
+ import https from "node:https";
6
+ import net from "node:net";
7
+ import { PromisedNetSockets } from "teleproto/extensions/PromisedNetSockets.js";
8
+ import { SocksClient } from "socks";
9
+
10
+ /**
11
+ * Создаёт TCP-туннель через HTTP/HTTPS прокси методом HTTP CONNECT.
12
+ * Поддерживает как прокси с Basic-авторизацией, так и без неё.
13
+ * @param {object} params
14
+ * @param {boolean} [params.isHttps=false] подключение к прокси-серверу по TLS
15
+ * @param {string} params.proxyHost адрес прокси-сервера
16
+ * @param {number} params.proxyPort порт прокси-сервера
17
+ * @param {string} params.targetHost адрес целевого сервера Telegram MTProto
18
+ * @param {number} params.targetPort порт целевого сервера Telegram MTProto
19
+ * @param {string} [params.username] логин для авторизации на прокси
20
+ * @param {string} [params.password] пароль для авторизации на прокси
21
+ * @param {number} [params.timeout=10000] таймаут установки соединения в миллисекундах
22
+ * @returns {Promise<import("node:net").Socket>}
23
+ */
24
+ export function createHttpConnectSocket({
25
+ isHttps = false,
26
+ proxyHost,
27
+ proxyPort,
28
+ targetHost,
29
+ targetPort,
30
+ username,
31
+ password,
32
+ timeout = 10000,
33
+ }) {
34
+ return new Promise((resolve, reject) => {
35
+ const clientModule = isHttps ? https : http;
36
+
37
+ const headers = {
38
+ Host: `${targetHost}:${targetPort}`,
39
+ "User-Agent": "TuiGram",
40
+ "Proxy-Connection": "Keep-Alive",
41
+ };
42
+
43
+ if (username || password) {
44
+ const auth = Buffer.from(`${username || ""}:${password || ""}`).toString("base64");
45
+ headers["Proxy-Authorization"] = `Basic ${auth}`;
46
+ }
47
+
48
+ const req = clientModule.request({
49
+ host: proxyHost,
50
+ port: proxyPort,
51
+ method: "CONNECT",
52
+ path: `${targetHost}:${targetPort}`,
53
+ headers,
54
+ timeout,
55
+ });
56
+
57
+ let finished = false;
58
+
59
+ const cleanup = () => {
60
+ req.removeAllListeners();
61
+ };
62
+
63
+ req.on("connect", (res, socket, head) => {
64
+ if (finished) return;
65
+ finished = true;
66
+ cleanup();
67
+
68
+ if (res.statusCode >= 200 && res.statusCode < 300) {
69
+ // Если в заголовке ответа уже были данные следующего протокола, возвращаем их в сокет
70
+ if (head && head.length > 0) {
71
+ socket.unshift(head);
72
+ }
73
+ resolve(socket);
74
+ } else if (res.statusCode === 407) {
75
+ socket.destroy();
76
+ reject(new Error("Ошибка авторизации на HTTP-прокси (407 Proxy Authentication Required)"));
77
+ } else {
78
+ socket.destroy();
79
+ reject(new Error(`HTTP-прокси вернул код ошибки: ${res.statusCode} ${res.statusMessage || ""}`.trim()));
80
+ }
81
+ });
82
+
83
+ req.on("response", (res) => {
84
+ if (finished) return;
85
+ finished = true;
86
+ cleanup();
87
+ if (res.statusCode === 407) {
88
+ reject(new Error("Ошибка авторизации на HTTP-прокси (407 Proxy Authentication Required)"));
89
+ } else {
90
+ reject(new Error(`HTTP-прокси вернул код ошибки: ${res.statusCode} ${res.statusMessage || ""}`.trim()));
91
+ }
92
+ });
93
+
94
+ req.on("timeout", () => {
95
+ if (finished) return;
96
+ finished = true;
97
+ cleanup();
98
+ req.destroy(new Error(`Таймаут подключения к HTTP-прокси (${proxyHost}:${proxyPort})`));
99
+ });
100
+
101
+ req.on("error", (err) => {
102
+ if (finished) return;
103
+ finished = true;
104
+ cleanup();
105
+ reject(new Error(`Не удалось подключиться к HTTP-прокси (${proxyHost}:${proxyPort}): ${err.message}`));
106
+ });
107
+
108
+ req.end();
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Расширенный сокет для teleproto с поддержкой HTTP CONNECT и SOCKS5/4 туннелирования.
114
+ */
115
+ export class TuiGramNetSockets extends PromisedNetSockets {
116
+ /**
117
+ * @param {object} [proxy]
118
+ * @param {number} [keepAliveInterval]
119
+ */
120
+ constructor(proxy, keepAliveInterval) {
121
+ // Передаём undefined в базовый класс, чтобы обойти валидацию socksType для HTTP-прокси
122
+ super(undefined, keepAliveInterval);
123
+ this.proxy = proxy;
124
+ }
125
+
126
+ /**
127
+ * Устанавливает сетевое соединение с дата-центром Telegram.
128
+ * @param {number} port
129
+ * @param {string} ip
130
+ * @returns {Promise<this>}
131
+ */
132
+ async connect(port, ip) {
133
+ this.chunks = [];
134
+ this.headOffset = 0;
135
+ this.available = 0;
136
+ let connected = false;
137
+
138
+ if (this.proxy) {
139
+ const proxyType = (this.proxy.type || "").toLowerCase();
140
+ const host = this.proxy.host || this.proxy.ip;
141
+ const proxyPort = Number(this.proxy.port);
142
+ const timeout = (this.proxy.timeout || 10) * 1000;
143
+
144
+ if (proxyType === "socks5" || proxyType === "socks4" || this.proxy.socksType) {
145
+ const socksType = this.proxy.socksType || (proxyType === "socks4" ? 4 : 5);
146
+ const info = await SocksClient.createConnection({
147
+ proxy: {
148
+ host,
149
+ port: proxyPort,
150
+ type: socksType,
151
+ userId: this.proxy.username,
152
+ password: this.proxy.password,
153
+ },
154
+ command: "connect",
155
+ timeout,
156
+ destination: {
157
+ host: ip,
158
+ port: port,
159
+ },
160
+ });
161
+ this.client = info.socket;
162
+ connected = true;
163
+ } else if (proxyType === "http" || proxyType === "https" || this.proxy.http) {
164
+ this.client = await createHttpConnectSocket({
165
+ isHttps: proxyType === "https",
166
+ proxyHost: host,
167
+ proxyPort: proxyPort,
168
+ targetHost: ip,
169
+ targetPort: port,
170
+ username: this.proxy.username,
171
+ password: this.proxy.password,
172
+ timeout,
173
+ });
174
+ connected = true;
175
+ } else {
176
+ throw new Error(`Неподдерживаемый тип прокси: ${this.proxy.type}`);
177
+ }
178
+ } else {
179
+ this.client = new net.Socket();
180
+ }
181
+
182
+ this.canRead = new Promise((resolve) => {
183
+ this.resolveRead = resolve;
184
+ });
185
+ this.closed = false;
186
+
187
+ return new Promise((resolve, reject) => {
188
+ if (!this.client) {
189
+ return reject(new Error("Сетевой сокет не инициализирован"));
190
+ }
191
+
192
+ const tune = (socket) => {
193
+ socket.setNoDelay(true);
194
+ socket.setKeepAlive(this.keepAliveInterval > 0, Math.max(0, this.keepAliveInterval));
195
+ };
196
+
197
+ if (connected) {
198
+ tune(this.client);
199
+ this.receive();
200
+ resolve(this);
201
+ } else {
202
+ this.client.connect(port, ip, () => {
203
+ tune(this.client);
204
+ this.receive();
205
+ resolve(this);
206
+ });
207
+ }
208
+
209
+ this.client.on("error", reject);
210
+ this.client.on("close", () => {
211
+ if (this.client && this.client.destroyed) {
212
+ if (this.resolveRead) {
213
+ this.resolveRead(false);
214
+ }
215
+ this.closed = true;
216
+ }
217
+ });
218
+ });
219
+ }
220
+
221
+ toString() {
222
+ return "TuiGramNetSocket";
223
+ }
224
+ }