mtproto-checker 0.1.1 โ 0.2.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/README.md +158 -244
- package/check.js +134 -11
- package/package.json +7 -3
- package/urls.txt +0 -4
package/README.md
CHANGED
|
@@ -1,335 +1,249 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ๐ mtproto-checker
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
handshake through every proxy, so a green result means the proxy is much more
|
|
5
|
-
likely to work in Telegram than with a plain TCP/TLS port check.
|
|
3
|
+
Telegram MTProto proxy health checker powered by **TDLib**. Performs a real `testProxy` handshake through each proxy โ the same protocol path tdesktop uses. If it says โ
, the proxy **actually works** in Telegram.
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## โก Features
|
|
8
6
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- ๐ Writes both a full JSON report and a ready-to-use TXT list
|
|
7
|
+
- ๐ค Real MTProto handshake (not just a port scan)
|
|
8
|
+
- ๐ก Check from remote URLs, local files, or single proxy links
|
|
9
|
+
- ๐ Multi-iteration filtering โ only survivors advance
|
|
10
|
+
- ๐ Built-in HTTP API server with Basic Auth
|
|
11
|
+
- ๐งน Auto de-duplication by `server:port:secret`
|
|
12
|
+
- ๐ Sorted output: working first, fastest on top
|
|
13
|
+
- ๐ Fake-TLS SNI extraction from `ee`-prefixed secrets
|
|
17
14
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
- Node.js 18+ recommended
|
|
21
|
-
- npm
|
|
22
|
-
- Telegram API credentials:
|
|
23
|
-
- `TG_API_ID`
|
|
24
|
-
- `TG_API_HASH`
|
|
25
|
-
|
|
26
|
-
Get API credentials from [my.telegram.org](https://my.telegram.org).
|
|
27
|
-
|
|
28
|
-
> No Telegram login or phone number is required. The credentials are only used
|
|
29
|
-
> to initialize TDLib before running `testProxy`.
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
15
|
+
## ๐ฆ Install
|
|
32
16
|
|
|
33
17
|
```bash
|
|
34
|
-
|
|
35
|
-
cd mtproto-checker
|
|
36
|
-
npm install
|
|
18
|
+
npm install mtproto-checker
|
|
37
19
|
```
|
|
38
20
|
|
|
39
|
-
|
|
21
|
+
Or clone locally:
|
|
40
22
|
|
|
41
23
|
```bash
|
|
42
|
-
|
|
24
|
+
git clone https://github.com/Tar4s/mtproto-checker.git
|
|
25
|
+
cd mtproto-checker
|
|
26
|
+
npm install
|
|
43
27
|
```
|
|
44
28
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
This project can also be published to GitHub Packages as
|
|
48
|
-
`@tar4s/mtproto-checker`. GitHub's npm registry requires scoped package names,
|
|
49
|
-
so the GitHub Packages workflow applies that scoped name during publishing.
|
|
50
|
-
|
|
51
|
-
To install from GitHub Packages:
|
|
29
|
+
GitHub Packages (scoped):
|
|
52
30
|
|
|
53
31
|
```bash
|
|
54
32
|
npm config set @tar4s:registry https://npm.pkg.github.com
|
|
55
33
|
npm install @tar4s/mtproto-checker
|
|
56
34
|
```
|
|
57
35
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Quick Start
|
|
62
|
-
|
|
63
|
-
Start the HTTP API server:
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
TG_API_ID=12345 \
|
|
67
|
-
TG_API_HASH=abcdef \
|
|
68
|
-
CHECK_AUTH_USER=admin \
|
|
69
|
-
CHECK_AUTH_PASSWORD=secret \
|
|
70
|
-
node check.js
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
By default it listens on port `3080`. Override it with `PORT`.
|
|
74
|
-
|
|
75
|
-
Check URLs listed in `urls.txt`:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
TG_API_ID=12345 TG_API_HASH=abcdef npm start -- --sources urls.txt
|
|
79
|
-
```
|
|
36
|
+
> **Requirements:** Node.js โฅ 18 ยท `TG_API_ID` + `TG_API_HASH` from [my.telegram.org](https://my.telegram.org)
|
|
37
|
+
> No phone login needed โ credentials only initialize TDLib.
|
|
80
38
|
|
|
81
|
-
|
|
39
|
+
## ๐ Environment Variables
|
|
82
40
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
41
|
+
| Variable | Required | Description |
|
|
42
|
+
|----------|:--------:|-------------|
|
|
43
|
+
| `TG_API_ID` | โ
| Telegram API ID |
|
|
44
|
+
| `TG_API_HASH` | โ
| Telegram API Hash |
|
|
45
|
+
| `CHECK_AUTH_USER` | ๐ | HTTP Basic Auth username (server mode) |
|
|
46
|
+
| `CHECK_AUTH_PASSWORD` | ๐ | HTTP Basic Auth password (server mode) |
|
|
47
|
+
| `PORT` | โ | Server port (default `3080`) |
|
|
86
48
|
|
|
87
|
-
|
|
49
|
+
## ๐ CLI Usage
|
|
88
50
|
|
|
89
51
|
```bash
|
|
90
|
-
TG_API_ID=12345 TG_API_HASH=abcdef check
|
|
52
|
+
TG_API_ID=12345 TG_API_HASH=abcdef node check.js [sources] [options]
|
|
91
53
|
```
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
### Input Methods
|
|
94
56
|
|
|
95
57
|
```bash
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```
|
|
58
|
+
# Single proxy link
|
|
59
|
+
node check.js --proxy "tg://proxy?server=1.2.3.4&port=443&secret=ee..."
|
|
99
60
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
### 1. Direct Proxy Link
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js \
|
|
108
|
-
--proxy 'https://t.me/proxy?server=1.2.3.4&port=443&secret=...'
|
|
109
|
-
```
|
|
61
|
+
# Remote URLs (positional or --url flag, repeatable)
|
|
62
|
+
node check.js https://example.com/proxies.txt
|
|
63
|
+
node check.js --url URL1 --url URL2
|
|
110
64
|
|
|
111
|
-
|
|
65
|
+
# File with source URLs (one per line, # comments ok)
|
|
66
|
+
node check.js --sources urls.txt
|
|
112
67
|
|
|
113
|
-
|
|
68
|
+
# Local proxy file
|
|
69
|
+
node check.js ./my-proxies.txt
|
|
114
70
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
https://example.com/more-proxies.txt
|
|
71
|
+
# Stdin
|
|
72
|
+
cat proxies.txt | node check.js
|
|
118
73
|
```
|
|
119
74
|
|
|
120
|
-
|
|
75
|
+
### โ๏ธ Options
|
|
121
76
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
77
|
+
| Flag | Default | Description |
|
|
78
|
+
|------|:-------:|-------------|
|
|
79
|
+
| `--proxy <link>` | โ | Check one proxy link directly |
|
|
80
|
+
| `--url <url>` | โ | Add a source URL (repeatable) |
|
|
81
|
+
| `--sources <file>` | โ | File of source URLs |
|
|
82
|
+
| `--dc <1-5>` | `2` | Data center for `testProxy` |
|
|
83
|
+
| `--timeout <sec>` | `10` | Per-proxy timeout |
|
|
84
|
+
| `--concurrency <n>` | `30` | Parallel checks (lower = more accurate ms) |
|
|
85
|
+
| `--iterations <n>` | `1` | Re-check rounds; only working proxies advance |
|
|
86
|
+
| `--out <prefix>` | `result` | Output prefix โ `.json` + `.txt` |
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js \
|
|
130
|
-
--url https://example.com/proxies.txt \
|
|
131
|
-
--url https://example.com/more-proxies.txt
|
|
132
|
-
```
|
|
88
|
+
### ๐ Output Files
|
|
133
89
|
|
|
134
|
-
|
|
90
|
+
| File | Content |
|
|
91
|
+
|------|---------|
|
|
92
|
+
| `result.json` | Full report: server, port, SNI, latency, error, link |
|
|
93
|
+
| `result.txt` | Working proxy links only, fastest first |
|
|
135
94
|
|
|
136
|
-
|
|
137
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js https://example.com/proxies.txt
|
|
138
|
-
```
|
|
95
|
+
## ๐ HTTP API Server
|
|
139
96
|
|
|
140
|
-
|
|
97
|
+
Start with **no arguments**:
|
|
141
98
|
|
|
142
99
|
```bash
|
|
143
|
-
TG_API_ID=12345 TG_API_HASH=abcdef
|
|
100
|
+
TG_API_ID=12345 TG_API_HASH=abcdef \
|
|
101
|
+
CHECK_AUTH_USER=admin CHECK_AUTH_PASSWORD=secret \
|
|
102
|
+
node check.js
|
|
144
103
|
```
|
|
145
104
|
|
|
146
|
-
### 5. stdin
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
cat proxies.txt | TG_API_ID=12345 TG_API_HASH=abcdef node check.js
|
|
150
105
|
```
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
## CLI Options
|
|
155
|
-
|
|
156
|
-
| Option | Default | Description |
|
|
157
|
-
| --- | ---: | --- |
|
|
158
|
-
| `--url <url>` | none | Add a remote proxy-list URL. Can be repeated. |
|
|
159
|
-
| `--proxy <link>` | none | Check one `tg://proxy` or `https://t.me/proxy` link directly. |
|
|
160
|
-
| `--sources <file>` | none | Read remote source URLs from a file, one URL per line. |
|
|
161
|
-
| `--dc <1-5>` | `2` | Telegram data center ID used for `testProxy`. |
|
|
162
|
-
| `--timeout <sec>` | `10` | Per-proxy TDLib timeout in seconds. Decimals are allowed. |
|
|
163
|
-
| `--concurrency <n>` | `30` | Number of proxies checked in parallel. Lower values can produce steadier latency numbers. |
|
|
164
|
-
| `--iterations <num>` | `1` | Number of check rounds. Each next round checks only proxies that passed the previous one. |
|
|
165
|
-
| `--out <prefix>` | `result` | Output file prefix. Writes `<prefix>.json` and `<prefix>.txt`. |
|
|
166
|
-
|
|
167
|
-
Environment variables:
|
|
168
|
-
|
|
169
|
-
| Variable | Required | Description |
|
|
170
|
-
| --- | --- | --- |
|
|
171
|
-
| `TG_API_ID` | yes | Telegram API ID from `my.telegram.org`. |
|
|
172
|
-
| `TG_API_HASH` | yes | Telegram API hash from `my.telegram.org`. |
|
|
173
|
-
| `CHECK_AUTH_USER` | HTTP server only | Basic auth username. |
|
|
174
|
-
| `CHECK_AUTH_PASSWORD` | HTTP server only | Basic auth password. |
|
|
175
|
-
| `PORT` | no | HTTP server port. Defaults to `3080`. |
|
|
176
|
-
|
|
177
|
-
## HTTP API
|
|
178
|
-
|
|
179
|
-
Running `node check.js` without CLI arguments starts the HTTP server. The server
|
|
180
|
-
requires Basic auth and exposes one endpoint:
|
|
181
|
-
|
|
182
|
-
```http
|
|
183
|
-
POST /check
|
|
184
|
-
Content-Type: application/json
|
|
185
|
-
Authorization: Basic ...
|
|
186
|
-
|
|
187
|
-
{ "url": "https://example.com/proxies.txt" }
|
|
106
|
+
[mtproto-checker] โก HTTP server listening on http://localhost:3080
|
|
107
|
+
[mtproto-checker] POST /check (Basic auth: admin:***)
|
|
188
108
|
```
|
|
189
109
|
|
|
190
|
-
|
|
191
|
-
`tg://proxy` / `https://t.me/proxy` link.
|
|
192
|
-
|
|
193
|
-
Example:
|
|
110
|
+
### `POST /check`
|
|
194
111
|
|
|
195
112
|
```bash
|
|
196
|
-
curl -u admin:secret \
|
|
197
|
-
-H
|
|
198
|
-
-d '{"url":"https://example.com/proxies.txt"}'
|
|
199
|
-
http://127.0.0.1:3080/check
|
|
113
|
+
curl -u admin:secret http://localhost:3080/check \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-d '{"url": "https://example.com/proxies.txt", "iterations": 2, "concurrency": 20}'
|
|
200
116
|
```
|
|
201
117
|
|
|
202
|
-
|
|
118
|
+
**Request body:**
|
|
203
119
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
```
|
|
120
|
+
| Field | Type | Default | Description |
|
|
121
|
+
|-------|------|:-------:|-------------|
|
|
122
|
+
| `url` | string | โ | Proxy list URL or single `tg://proxy` link |
|
|
123
|
+
| `iterations` | int | `1` | Check rounds |
|
|
124
|
+
| `concurrency` | int | `30` | Parallel checks |
|
|
210
125
|
|
|
211
|
-
Response
|
|
126
|
+
**Response:**
|
|
212
127
|
|
|
213
128
|
```json
|
|
214
129
|
{
|
|
215
130
|
"url": "https://example.com/proxies.txt",
|
|
216
|
-
"
|
|
217
|
-
"
|
|
131
|
+
"iterations": 2,
|
|
132
|
+
"concurrency": 20,
|
|
133
|
+
"count": 150,
|
|
134
|
+
"working": 42,
|
|
218
135
|
"results": [
|
|
219
|
-
{
|
|
220
|
-
"server": "1.2.3.4",
|
|
221
|
-
"port": 443,
|
|
222
|
-
"sni": "example.com",
|
|
223
|
-
"ok": true,
|
|
224
|
-
"ms": 841,
|
|
225
|
-
"error": null,
|
|
226
|
-
"link": "tg://proxy?server=1.2.3.4&port=443&secret=..."
|
|
227
|
-
}
|
|
136
|
+
{ "server": "1.2.3.4", "port": 443, "sni": "example.com", "ok": true, "ms": 312, "error": null, "link": "tg://proxy?..." }
|
|
228
137
|
]
|
|
229
138
|
}
|
|
230
139
|
```
|
|
231
140
|
|
|
232
|
-
|
|
141
|
+
**Error codes:** `400` bad request ยท `401` unauthorized ยท `404` wrong endpoint ยท `405` wrong method ยท `502` upstream fetch failed
|
|
233
142
|
|
|
234
|
-
|
|
143
|
+
## ๐ Library API
|
|
235
144
|
|
|
236
|
-
|
|
237
|
-
|
|
145
|
+
```js
|
|
146
|
+
const { checkProxyLink, checkProxiesFromURIs, startServer } = require('mtproto-checker')
|
|
147
|
+
```
|
|
238
148
|
|
|
239
|
-
|
|
149
|
+
### `checkProxyLink(link, opts)` โ `Promise<Array>`
|
|
240
150
|
|
|
241
|
-
|
|
242
|
-
{
|
|
243
|
-
"server": "1.2.3.4",
|
|
244
|
-
"port": 443,
|
|
245
|
-
"sni": "example.com",
|
|
246
|
-
"ok": true,
|
|
247
|
-
"ms": 841,
|
|
248
|
-
"error": null,
|
|
249
|
-
"link": "tg://proxy?server=1.2.3.4&port=443&secret=..."
|
|
250
|
-
}
|
|
251
|
-
```
|
|
151
|
+
Check a single `tg://proxy` or `https://t.me/proxy` link.
|
|
252
152
|
|
|
253
|
-
|
|
153
|
+
```js
|
|
154
|
+
const results = await checkProxyLink(
|
|
155
|
+
'tg://proxy?server=1.2.3.4&port=443&secret=ee...',
|
|
156
|
+
{ apiId: 12345, apiHash: 'abcdef' }
|
|
157
|
+
)
|
|
158
|
+
```
|
|
254
159
|
|
|
255
|
-
```bash
|
|
256
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt --out fresh
|
|
257
160
|
```
|
|
161
|
+
[mtproto-checker] Checking 1.2.3.4:443 [example.com]...
|
|
162
|
+
[mtproto-checker] โ 312ms
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `checkProxiesFromURIs(uris, opts)` โ `Promise<Array>`
|
|
258
166
|
|
|
259
|
-
|
|
167
|
+
Check proxies from remote URLs, local files, or both. Auto-detects type per entry.
|
|
260
168
|
|
|
261
|
-
|
|
169
|
+
```js
|
|
170
|
+
// Remote
|
|
171
|
+
const results = await checkProxiesFromURIs(
|
|
172
|
+
'https://example.com/proxies.txt',
|
|
173
|
+
{ apiId: 12345, apiHash: 'abcdef' }
|
|
174
|
+
)
|
|
262
175
|
|
|
263
|
-
|
|
176
|
+
// Local
|
|
177
|
+
const results = await checkProxiesFromURIs('./proxies.txt', opts)
|
|
264
178
|
|
|
265
|
-
|
|
266
|
-
|
|
179
|
+
// Mix
|
|
180
|
+
const results = await checkProxiesFromURIs([
|
|
181
|
+
'https://example.com/list1.txt',
|
|
182
|
+
'./local-list.txt',
|
|
183
|
+
'https://example.com/list2.txt'
|
|
184
|
+
], { apiId: 12345, apiHash: 'abcdef', iterations: 2, concurrency: 20 })
|
|
267
185
|
```
|
|
268
186
|
|
|
269
|
-
More conservative latency check:
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt --concurrency 10 --timeout 15
|
|
273
187
|
```
|
|
188
|
+
[mtproto-checker] Loading 3 source(s)...
|
|
189
|
+
โ https://example.com/list1.txt
|
|
190
|
+
โ ./local-list.txt
|
|
191
|
+
โ https://example.com/list2.txt
|
|
192
|
+
[mtproto-checker] Checking 150 proxies (dc=2, timeout=10s, concurrency=30, iterations=2)...
|
|
274
193
|
|
|
275
|
-
|
|
194
|
+
[ 1/150] โ 312ms 1.2.3.4:443 [example.com]
|
|
195
|
+
[ 2/150] โ Timeout 5.6.7.8:443
|
|
196
|
+
...
|
|
276
197
|
|
|
277
|
-
|
|
278
|
-
TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt --iterations 3 --out stable
|
|
198
|
+
[mtproto-checker] Done: 42/150 working.
|
|
279
199
|
```
|
|
280
200
|
|
|
281
|
-
|
|
201
|
+
### `startServer(opts)` โ `Promise<http.Server>`
|
|
282
202
|
|
|
283
|
-
|
|
284
|
-
|
|
203
|
+
Start the HTTP API server programmatically.
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
const server = await startServer({
|
|
207
|
+
apiId: 12345,
|
|
208
|
+
apiHash: 'abcdef',
|
|
209
|
+
user: 'admin',
|
|
210
|
+
password: 'secret',
|
|
211
|
+
port: 8080
|
|
212
|
+
})
|
|
285
213
|
```
|
|
286
214
|
|
|
287
|
-
|
|
215
|
+
All fields are optional โ falls back to env vars if omitted.
|
|
288
216
|
|
|
289
|
-
|
|
290
|
-
pbpaste | TG_API_ID=12345 TG_API_HASH=abcdef node check.js --out pasted
|
|
291
|
-
```
|
|
217
|
+
### `opts` Reference
|
|
292
218
|
|
|
293
|
-
|
|
219
|
+
| Key | Type | Default | Description |
|
|
220
|
+
|-----|------|:-------:|-------------|
|
|
221
|
+
| `apiId` | number | โ | Telegram API ID |
|
|
222
|
+
| `apiHash` | string | โ | Telegram API Hash |
|
|
223
|
+
| `dc` | number | `2` | Data center (1โ5) |
|
|
224
|
+
| `timeout` | number | `10` | Timeout in seconds |
|
|
225
|
+
| `concurrency` | number | `30` | Parallel checks |
|
|
226
|
+
| `iterations` | number | `1` | Check rounds |
|
|
227
|
+
| `onProgress` | function | โ | `(proxy, res, index, total) => void` |
|
|
294
228
|
|
|
295
|
-
|
|
296
|
-
const { checkProxiesFromUrls } = require('./check')
|
|
297
|
-
|
|
298
|
-
const results = await checkProxiesFromUrls(
|
|
299
|
-
['https://example.com/proxies.txt'],
|
|
300
|
-
{
|
|
301
|
-
apiId: Number(process.env.TG_API_ID),
|
|
302
|
-
apiHash: process.env.TG_API_HASH,
|
|
303
|
-
dc: 2,
|
|
304
|
-
timeout: 10,
|
|
305
|
-
concurrency: 30
|
|
306
|
-
}
|
|
307
|
-
)
|
|
229
|
+
## ๐งฉ Proxy Link Formats
|
|
308
230
|
|
|
309
|
-
|
|
231
|
+
```
|
|
232
|
+
tg://proxy?server=1.2.3.4&port=443&secret=ee...
|
|
233
|
+
https://t.me/proxy?server=1.2.3.4&port=443&secret=ee...
|
|
310
234
|
```
|
|
311
235
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
- `checkProxiesFromUrls(urls, opts)`
|
|
315
|
-
- `loadProxiesFromUrls(urls)`
|
|
316
|
-
- `checkProxies(proxies, opts)`
|
|
317
|
-
- `mergeProxies(texts)`
|
|
318
|
-
- `parseLink(line)`
|
|
319
|
-
- `normalizeSecret(secret)`
|
|
320
|
-
- `faketlsSni(hexSecret)`
|
|
236
|
+
Secrets: hex (`ee...`, `dd...`), plain hex, or base64url โ auto-detected. `tg://socks` links are ignored.
|
|
321
237
|
|
|
322
|
-
##
|
|
238
|
+
## ๐ Troubleshooting
|
|
323
239
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
- Only MTProto proxy links are checked. `tg://socks` links are ignored.
|
|
331
|
-
- Temporary TDLib files are created in `.proxy-checker-td/` and removed after the run.
|
|
240
|
+
| Problem | Fix |
|
|
241
|
+
|---------|-----|
|
|
242
|
+
| `Set TG_API_ID and TG_API_HASH` | Export both env vars |
|
|
243
|
+
| Noisy latency | Reduce `--concurrency` |
|
|
244
|
+
| Need stable proxies only | Increase `--iterations` |
|
|
245
|
+
| TDLib leftover files | `.proxy-checker-td/` is auto-cleaned after each run |
|
|
332
246
|
|
|
333
|
-
## License
|
|
247
|
+
## ๐ License
|
|
334
248
|
|
|
335
|
-
|
|
249
|
+
ISC
|
package/check.js
CHANGED
|
@@ -415,6 +415,102 @@ async function checkSingleUrl(url, opts) {
|
|
|
415
415
|
return checker(proxies, opts)
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Load proxies from a local file path.
|
|
420
|
+
* @param {string} filePath - path to a text file with proxy links (one per line)
|
|
421
|
+
* @returns {Array} de-duplicated parsed proxies
|
|
422
|
+
*/
|
|
423
|
+
function loadProxiesFromFile(filePath) {
|
|
424
|
+
const text = fs.readFileSync(filePath, 'utf8')
|
|
425
|
+
return mergeProxies([text])
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Check proxies from URIs โ supports both remote URLs (http/https) and local
|
|
430
|
+
* file paths. Detects type automatically per entry.
|
|
431
|
+
* @param {string|string[]} uris - URL(s) or file path(s) with proxy links
|
|
432
|
+
* @param {{apiId: number, apiHash: string, dc?: number, timeout?: number, concurrency?: number, iterations?: number, onProgress?: Function}} opts
|
|
433
|
+
* @returns {Promise<Array<{proxy: object, ok: boolean, ms: number, error: string|null}>>}
|
|
434
|
+
*/
|
|
435
|
+
async function checkProxiesFromURIs(uris, opts) {
|
|
436
|
+
const list = Array.isArray(uris) ? uris : [uris]
|
|
437
|
+
console.error(`[mtproto-checker] Loading ${list.length} source(s)...`)
|
|
438
|
+
const texts = await Promise.all(list.map(uri => {
|
|
439
|
+
if (/^https?:\/\//i.test(uri)) {
|
|
440
|
+
console.error(` โ ${uri}`)
|
|
441
|
+
return fetchText(uri).catch(err => { console.error(` โ Skipping ${uri}: ${err.message}`); return '' })
|
|
442
|
+
}
|
|
443
|
+
console.error(` โ ${uri}`)
|
|
444
|
+
return Promise.resolve(fs.readFileSync(uri, 'utf8'))
|
|
445
|
+
}))
|
|
446
|
+
const proxies = mergeProxies(texts)
|
|
447
|
+
if (proxies.length === 0) {
|
|
448
|
+
console.error('[mtproto-checker] No valid proxy links found.')
|
|
449
|
+
return []
|
|
450
|
+
}
|
|
451
|
+
const iterations = opts.iterations ?? 1
|
|
452
|
+
console.error(`[mtproto-checker] Checking ${proxies.length} proxies (dc=${opts.dc ?? 2}, timeout=${opts.timeout ?? 10}s, concurrency=${opts.concurrency ?? 30}, iterations=${iterations})...\n`)
|
|
453
|
+
const results = await runIterativeChecks(proxies, iterations, (batch, iteration) => {
|
|
454
|
+
if (iterations > 1) console.error(`[mtproto-checker] Iteration ${iteration}/${iterations}: ${batch.length} proxies\n`)
|
|
455
|
+
return checkProxies(batch, {
|
|
456
|
+
...opts,
|
|
457
|
+
onProgress: (proxy, res, index, total) => {
|
|
458
|
+
const tag = res.ok ? `โ ${String(res.ms).padStart(5)}ms` : `โ ${res.error}`
|
|
459
|
+
const sni = proxy.sni ? ` [${proxy.sni}]` : ''
|
|
460
|
+
console.error(` [${String(index + 1).padStart(3)}/${total}] ${tag} ${proxy.server}:${proxy.port}${sni}`)
|
|
461
|
+
if (opts.onProgress) opts.onProgress(proxy, res, index, total)
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
})
|
|
465
|
+
const working = results.filter(c => c.ok).length
|
|
466
|
+
console.error(`\n[mtproto-checker] Done: ${working}/${proxies.length} working.`)
|
|
467
|
+
return results
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Check a single tg://proxy or t.me/proxy link via real MTProto handshake.
|
|
472
|
+
* @param {string} link - proxy link (tg://proxy?... or https://t.me/proxy?...)
|
|
473
|
+
* @param {{apiId: number, apiHash: string, dc?: number, timeout?: number, iterations?: number, onProgress?: Function}} opts
|
|
474
|
+
* @returns {Promise<Array<{proxy: object, ok: boolean, ms: number, error: string|null}>>}
|
|
475
|
+
* @throws if the link is not a valid MTProto proxy link
|
|
476
|
+
*/
|
|
477
|
+
async function checkProxyLink(link, opts) {
|
|
478
|
+
const proxy = parseLink(link)
|
|
479
|
+
if (!proxy) throw new Error('Invalid proxy link. Expected tg://proxy?... or https://t.me/proxy?...')
|
|
480
|
+
const sni = proxy.sni ? ` [${proxy.sni}]` : ''
|
|
481
|
+
console.error(`[mtproto-checker] Checking ${proxy.server}:${proxy.port}${sni}...`)
|
|
482
|
+
const results = await runIterativeChecks([proxy], opts.iterations ?? 1, batch => checkProxies(batch, opts))
|
|
483
|
+
const r = results[0]
|
|
484
|
+
if (r)
|
|
485
|
+
console.error(`[mtproto-checker] ${r.ok ? `โ ${r.ms}ms` : `โ ${r.error}`}`)
|
|
486
|
+
return results
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function loadSingleUrlProxies(url, opts = {}) {
|
|
490
|
+
const directProxy = parseLink(url)
|
|
491
|
+
if (directProxy) return [directProxy]
|
|
492
|
+
|
|
493
|
+
let parsed
|
|
494
|
+
try {
|
|
495
|
+
parsed = new URL(url)
|
|
496
|
+
} catch {
|
|
497
|
+
throw new Error('url must be a proxy link or an http or https URL')
|
|
498
|
+
}
|
|
499
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
500
|
+
throw new Error('url must be a proxy link or an http or https URL')
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const fetcher = opts.fetcher || fetchText
|
|
504
|
+
const text = await fetcher(url)
|
|
505
|
+
return mergeProxies([text])
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async function checkRequestUrl(url, opts) {
|
|
509
|
+
const proxies = await loadSingleUrlProxies(url, opts)
|
|
510
|
+
const checker = opts.checker || checkProxies
|
|
511
|
+
return runIterativeChecks(proxies, opts.iterations ?? 1, batch => checker(batch, opts))
|
|
512
|
+
}
|
|
513
|
+
|
|
418
514
|
async function resolveInputProxies(opts, deps = {}) {
|
|
419
515
|
const readFile = deps.readFile || (file => fs.readFileSync(file, 'utf8'))
|
|
420
516
|
const readInputFn = deps.readInput || readInput
|
|
@@ -554,11 +650,23 @@ function createServer({ auth, checkUrl, logger = console.error }) {
|
|
|
554
650
|
jsonResponse(res, statusCode, { error: 'Request body must include url' })
|
|
555
651
|
return
|
|
556
652
|
}
|
|
653
|
+
const iterations = body.iterations === undefined ? 1 : body.iterations
|
|
654
|
+
if (!Number.isInteger(iterations) || iterations < 1) {
|
|
655
|
+
statusCode = 400
|
|
656
|
+
jsonResponse(res, statusCode, { error: 'iterations must be a positive integer' })
|
|
657
|
+
return
|
|
658
|
+
}
|
|
659
|
+
const concurrency = body.concurrency === undefined ? 30 : body.concurrency
|
|
660
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
661
|
+
statusCode = 400
|
|
662
|
+
jsonResponse(res, statusCode, { error: 'concurrency must be a positive integer' })
|
|
663
|
+
return
|
|
664
|
+
}
|
|
557
665
|
|
|
558
666
|
const url = body.url.trim()
|
|
559
667
|
let results
|
|
560
668
|
try {
|
|
561
|
-
results = await checkUrl(url)
|
|
669
|
+
results = await checkUrl(url, { iterations, concurrency })
|
|
562
670
|
} catch (err) {
|
|
563
671
|
statusCode = 502
|
|
564
672
|
jsonResponse(res, statusCode, {
|
|
@@ -571,6 +679,8 @@ function createServer({ auth, checkUrl, logger = console.error }) {
|
|
|
571
679
|
statusCode = 200
|
|
572
680
|
jsonResponse(res, statusCode, {
|
|
573
681
|
url,
|
|
682
|
+
iterations,
|
|
683
|
+
concurrency,
|
|
574
684
|
count: report.length,
|
|
575
685
|
working: report.filter(item => item.ok).length,
|
|
576
686
|
results: report
|
|
@@ -590,20 +700,31 @@ function shouldStartServer(argv) {
|
|
|
590
700
|
return argv.length === 0
|
|
591
701
|
}
|
|
592
702
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
703
|
+
/**
|
|
704
|
+
* Start the HTTP API server for proxy checking.
|
|
705
|
+
* @param {{apiId?: number, apiHash?: string, user?: string, password?: string, port?: number}} opts
|
|
706
|
+
* All fields fall back to environment variables if omitted.
|
|
707
|
+
* @returns {Promise<http.Server>} the listening server instance
|
|
708
|
+
*/
|
|
709
|
+
async function startServer(opts = {}) {
|
|
710
|
+
const apiId = opts.apiId ?? parseInt(process.env.TG_API_ID, 10)
|
|
711
|
+
const apiHash = opts.apiHash ?? process.env.TG_API_HASH
|
|
712
|
+
const user = opts.user ?? process.env.CHECK_AUTH_USER
|
|
713
|
+
const password = opts.password ?? process.env.CHECK_AUTH_PASSWORD
|
|
714
|
+
const port = opts.port ?? parseInt(process.env.PORT || '3080', 10)
|
|
599
715
|
|
|
600
716
|
if (!apiId || !apiHash) throw new Error('Set TG_API_ID and TG_API_HASH (get them at https://my.telegram.org).')
|
|
601
717
|
if (!user || !password) throw new Error('Set CHECK_AUTH_USER and CHECK_AUTH_PASSWORD for HTTP Basic auth.')
|
|
602
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('
|
|
718
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('port must be a valid TCP port (1-65535).')
|
|
603
719
|
|
|
604
720
|
const server = createServer({
|
|
605
721
|
auth: { user, password },
|
|
606
|
-
checkUrl: async url =>
|
|
722
|
+
checkUrl: async (url, requestOpts) => checkRequestUrl(url, {
|
|
723
|
+
apiId,
|
|
724
|
+
apiHash,
|
|
725
|
+
iterations: requestOpts.iterations,
|
|
726
|
+
concurrency: requestOpts.concurrency
|
|
727
|
+
})
|
|
607
728
|
})
|
|
608
729
|
|
|
609
730
|
await new Promise((resolve, reject) => {
|
|
@@ -614,7 +735,8 @@ async function startServer(env = process.env) {
|
|
|
614
735
|
})
|
|
615
736
|
})
|
|
616
737
|
|
|
617
|
-
console.error(`
|
|
738
|
+
console.error(`[mtproto-checker] โก HTTP server listening on http://localhost:${port}`)
|
|
739
|
+
console.error(`[mtproto-checker] POST /check (Basic auth: ${user}:***)`)
|
|
618
740
|
return server
|
|
619
741
|
}
|
|
620
742
|
|
|
@@ -681,7 +803,8 @@ async function main() {
|
|
|
681
803
|
process.exit(0)
|
|
682
804
|
}
|
|
683
805
|
|
|
684
|
-
module.exports = {
|
|
806
|
+
module.exports = { checkProxyLink, checkProxiesFromURIs, startServer }
|
|
807
|
+
module.exports._internals = { checkRequestUrl, checkSingleUrl, configureTdlibOnce, createServer, parseArgs, resolveInputProxies, runIterativeChecks, shouldStartServer }
|
|
685
808
|
|
|
686
809
|
if (require.main === module) (shouldStartServer(process.argv.slice(2)) ? startServer() : main()).catch(err => {
|
|
687
810
|
console.error(err)
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mtproto-checker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Check Telegram MTProto proxies via a real TDLib handshake (testProxy), like tdesktop does",
|
|
5
|
+
"main": "check.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./check.js"
|
|
8
|
+
},
|
|
5
9
|
"repository": {
|
|
6
10
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/Tar4s/mtproto-checker"
|
|
11
|
+
"url": "git+https://github.com/Tar4s/mtproto-checker.git"
|
|
8
12
|
},
|
|
9
13
|
"bugs": {
|
|
10
14
|
"url": "https://github.com/Tar4s/mtproto-checker/issues"
|
|
@@ -22,7 +26,7 @@
|
|
|
22
26
|
"start": "node check.js"
|
|
23
27
|
},
|
|
24
28
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
29
|
+
"node": ">=18"
|
|
26
30
|
},
|
|
27
31
|
"dependencies": {
|
|
28
32
|
"tdl": "^8.0.2",
|
package/urls.txt
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
https://raw.githubusercontent.com/SoliSpirit/mtproto/refs/heads/master/all_proxies.txt
|
|
2
|
-
https://raw.githubusercontent.com/kort0881/telegram-proxy-collector/refs/heads/main/proxy_all.txt
|
|
3
|
-
https://raw.githubusercontent.com/Surfboardv2ray/TGProto/refs/heads/main/proxies.txt
|
|
4
|
-
https://raw.githubusercontent.com/Therealwh/MTPproxyLIST/refs/heads/main/verified/proxy_all_tme_verified.txt
|