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.
Files changed (4) hide show
  1. package/README.md +158 -244
  2. package/check.js +134 -11
  3. package/package.json +7 -3
  4. package/urls.txt +0 -4
package/README.md CHANGED
@@ -1,335 +1,249 @@
1
- # MTProto Checker โšก
1
+ # ๐Ÿ” mtproto-checker
2
2
 
3
- Fast Telegram MTProto proxy checker powered by TDLib. It does a real `testProxy`
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
- ## What It Does
5
+ ## โšก Features
8
6
 
9
- - โœ… Parses `tg://proxy` and `https://t.me/proxy` links
10
- - ๐ŸŒ Loads proxy lists from remote URLs, local files, or `stdin`
11
- - ๐Ÿงน Removes duplicates by `server:port:secret`
12
- - ๐Ÿ” Supports hex and base64url MTProto secrets
13
- - ๐Ÿ•ต๏ธ Extracts Fake-TLS SNI from `ee...` secrets and supports padded `dd...` secrets
14
- - ๐Ÿš€ Checks proxies concurrently via TDLib `testProxy`
15
- - ๐Ÿ’ช Re-checks survivors with `--iterations` to find the most stable proxies
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
- ## Requirements
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
- git clone git@github.com:Tar4s/mtproto-checker.git
35
- cd mtproto-checker
36
- npm install
18
+ npm install mtproto-checker
37
19
  ```
38
20
 
39
- Optional, if you want the `check-proxies` command available locally:
21
+ Or clone locally:
40
22
 
41
23
  ```bash
42
- npm link
24
+ git clone https://github.com/Tar4s/mtproto-checker.git
25
+ cd mtproto-checker
26
+ npm install
43
27
  ```
44
28
 
45
- ### GitHub Packages
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
- Private packages require authentication with a GitHub personal access token that
59
- has `read:packages`.
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
- Equivalent direct Node.js run:
39
+ ## ๐Ÿ”‘ Environment Variables
82
40
 
83
- ```bash
84
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt
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
- With `npm link`:
49
+ ## ๐Ÿš€ CLI Usage
88
50
 
89
51
  ```bash
90
- TG_API_ID=12345 TG_API_HASH=abcdef check-proxies --sources urls.txt
52
+ TG_API_ID=12345 TG_API_HASH=abcdef node check.js [sources] [options]
91
53
  ```
92
54
 
93
- Check one proxy link directly:
55
+ ### Input Methods
94
56
 
95
57
  ```bash
96
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js \
97
- --proxy 'tg://proxy?server=quackton.life&port=443&secret=7mX8dVOh9cqLULccAVs4ciR5YW5kZXgucnU'
98
- ```
58
+ # Single proxy link
59
+ node check.js --proxy "tg://proxy?server=1.2.3.4&port=443&secret=ee..."
99
60
 
100
- ## Input Sources
101
-
102
- You can provide proxies in several ways.
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
- ### 2. Source URL File
65
+ # File with source URLs (one per line, # comments ok)
66
+ node check.js --sources urls.txt
112
67
 
113
- `urls.txt` contains one remote text-list URL per line:
68
+ # Local proxy file
69
+ node check.js ./my-proxies.txt
114
70
 
115
- ```txt
116
- https://example.com/proxies.txt
117
- https://example.com/more-proxies.txt
71
+ # Stdin
72
+ cat proxies.txt | node check.js
118
73
  ```
119
74
 
120
- Run:
75
+ ### โš™๏ธ Options
121
76
 
122
- ```bash
123
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt
124
- ```
125
-
126
- ### 3. One Or More Remote URLs
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
- ```bash
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
- Positional HTTP URLs also work:
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
- ```bash
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
- ### 4. Local Proxy File
97
+ Start with **no arguments**:
141
98
 
142
99
  ```bash
143
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js proxies.txt
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
- Input files may contain blank lines and `#` comments.
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
- The `url` field accepts either a remote `http(s)` proxy list or one direct
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 'content-type: application/json' \
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
- Direct proxy link example:
118
+ **Request body:**
203
119
 
204
- ```bash
205
- curl -u admin:secret \
206
- -H 'content-type: application/json' \
207
- -d '{"url":"tg://proxy?server=quackton.life&port=443&secret=7mX8dVOh9cqLULccAVs4ciR5YW5kZXgucnU"}' \
208
- http://127.0.0.1:3080/check
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
- "count": 1,
217
- "working": 1,
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
- ## Output
141
+ **Error codes:** `400` bad request ยท `401` unauthorized ยท `404` wrong endpoint ยท `405` wrong method ยท `502` upstream fetch failed
233
142
 
234
- By default the checker writes:
143
+ ## ๐Ÿ“š Library API
235
144
 
236
- - `result.json` โ€” full report for the final completed round
237
- - `result.txt` โ€” working proxy links from the final completed round, fastest first
145
+ ```js
146
+ const { checkProxyLink, checkProxiesFromURIs, startServer } = require('mtproto-checker')
147
+ ```
238
148
 
239
- Example JSON item:
149
+ ### `checkProxyLink(link, opts)` โ†’ `Promise<Array>`
240
150
 
241
- ```json
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
- Use a custom prefix:
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
- This creates `fresh.json` and `fresh.txt`.
167
+ Check proxies from remote URLs, local files, or both. Auto-detects type per entry.
260
168
 
261
- ## Practical Examples
169
+ ```js
170
+ // Remote
171
+ const results = await checkProxiesFromURIs(
172
+ 'https://example.com/proxies.txt',
173
+ { apiId: 12345, apiHash: 'abcdef' }
174
+ )
262
175
 
263
- Fast broad scan:
176
+ // Local
177
+ const results = await checkProxiesFromURIs('./proxies.txt', opts)
264
178
 
265
- ```bash
266
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt --concurrency 80 --timeout 7
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
- Find the most stable proxies across several rounds:
194
+ [ 1/150] โœ“ 312ms 1.2.3.4:443 [example.com]
195
+ [ 2/150] โœ— Timeout 5.6.7.8:443
196
+ ...
276
197
 
277
- ```bash
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
- Test against another Telegram DC:
201
+ ### `startServer(opts)` โ†’ `Promise<http.Server>`
282
202
 
283
- ```bash
284
- TG_API_ID=12345 TG_API_HASH=abcdef node check.js --sources urls.txt --dc 4
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
- Check a pasted list:
215
+ All fields are optional โ€” falls back to env vars if omitted.
288
216
 
289
- ```bash
290
- pbpaste | TG_API_ID=12345 TG_API_HASH=abcdef node check.js --out pasted
291
- ```
217
+ ### `opts` Reference
292
218
 
293
- ## Programmatic Usage
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
- ```js
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
- console.log(results)
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
- Exported helpers:
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
- ## Notes & Troubleshooting
238
+ ## ๐Ÿ›  Troubleshooting
323
239
 
324
- - If you see `Set TG_API_ID and TG_API_HASH`, export both credentials or prefix
325
- the command with them.
326
- - If remote URLs fail on old Node.js versions, upgrade to Node.js 18+.
327
- - If checks are noisy, reduce `--concurrency`.
328
- - If you want only resilient proxies, increase `--iterations`; `result.txt`
329
- will contain proxies that survived the final round.
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
- No license file is currently included.
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
- async function startServer(env = process.env) {
594
- const apiId = parseInt(env.TG_API_ID, 10)
595
- const apiHash = env.TG_API_HASH
596
- const user = env.CHECK_AUTH_USER
597
- const password = env.CHECK_AUTH_PASSWORD
598
- const port = parseInt(env.PORT || '3080', 10)
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('PORT must be a valid TCP port.')
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 => checkSingleUrl(url, { apiId, apiHash })
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(`MTProto checker HTTP server listening on :${port}`)
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 = { checkSingleUrl, configureTdlibOnce, createServer, parseArgs, resolveInputProxies, checkProxiesFromUrls, loadProxiesFromUrls, checkProxies, runIterativeChecks, mergeProxies, parseLink, normalizeSecret, faketlsSni, shouldStartServer, startServer }
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.1.1",
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": ">=16"
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