mtproto-checker 0.1.2 โ 0.3.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 +177 -126
- package/check.js +113 -19
- package/package.json +1 -1
- package/urls.txt +0 -4
package/README.md
CHANGED
|
@@ -1,198 +1,249 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ๐ mtproto-checker
|
|
2
2
|
|
|
3
|
-
Telegram MTProto proxy checker powered by TDLib
|
|
4
|
-
handshake through each proxy, so a successful result is closer to "works in
|
|
5
|
-
Telegram" than 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
|
-
## Features
|
|
5
|
+
## โก Features
|
|
8
6
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
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
|
|
16
14
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
- Node.js 18+
|
|
20
|
-
- Telegram API credentials: `TG_API_ID` and `TG_API_HASH`
|
|
21
|
-
|
|
22
|
-
Get credentials from [my.telegram.org](https://my.telegram.org). No Telegram
|
|
23
|
-
login or phone number is required; credentials are only used to initialize TDLib.
|
|
24
|
-
|
|
25
|
-
## Install
|
|
15
|
+
## ๐ฆ Install
|
|
26
16
|
|
|
27
17
|
```bash
|
|
28
18
|
npm install mtproto-checker
|
|
29
19
|
```
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
Or clone locally:
|
|
32
22
|
|
|
33
23
|
```bash
|
|
34
|
-
git clone
|
|
24
|
+
git clone https://github.com/Tar4s/mtproto-checker.git
|
|
35
25
|
cd mtproto-checker
|
|
36
26
|
npm install
|
|
37
27
|
```
|
|
38
28
|
|
|
39
|
-
GitHub Packages
|
|
29
|
+
GitHub Packages (scoped):
|
|
40
30
|
|
|
41
31
|
```bash
|
|
42
32
|
npm config set @tar4s:registry https://npm.pkg.github.com
|
|
43
33
|
npm install @tar4s/mtproto-checker
|
|
44
34
|
```
|
|
45
35
|
|
|
46
|
-
|
|
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.
|
|
47
38
|
|
|
48
|
-
##
|
|
39
|
+
## ๐ Environment Variables
|
|
40
|
+
|
|
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`) |
|
|
48
|
+
|
|
49
|
+
## ๐ CLI Usage
|
|
49
50
|
|
|
50
51
|
```bash
|
|
51
|
-
TG_API_ID=12345 TG_API_HASH=abcdef
|
|
52
|
+
TG_API_ID=12345 TG_API_HASH=abcdef node check.js [sources] [options]
|
|
52
53
|
```
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
### Input Methods
|
|
55
56
|
|
|
56
57
|
```bash
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
# Single proxy link
|
|
59
|
+
node check.js --proxy "tg://proxy?server=1.2.3.4&port=443&secret=ee..."
|
|
60
|
+
|
|
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
|
|
60
64
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
--url https://example.com/proxies.txt \
|
|
64
|
-
--url https://example.com/more.txt
|
|
65
|
+
# File with source URLs (one per line, # comments ok)
|
|
66
|
+
node check.js --sources urls.txt
|
|
65
67
|
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
# Local proxy file
|
|
69
|
+
node check.js ./my-proxies.txt
|
|
70
|
+
|
|
71
|
+
# Stdin
|
|
72
|
+
cat proxies.txt | node check.js
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
Options
|
|
75
|
+
### โ๏ธ Options
|
|
76
|
+
|
|
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` |
|
|
73
87
|
|
|
74
|
-
|
|
75
|
-
| --- | ---: | --- |
|
|
76
|
-
| `--proxy <link>` | none | Check one `tg://proxy` or `https://t.me/proxy` link directly. |
|
|
77
|
-
| `--url <url>` | none | Add a remote proxy-list URL. Repeatable. |
|
|
78
|
-
| `--sources <file>` | none | Read remote source URLs from a file. |
|
|
79
|
-
| `--dc <1-5>` | `2` | Telegram data center used for `testProxy`. |
|
|
80
|
-
| `--timeout <sec>` | `10` | Per-proxy TDLib timeout. |
|
|
81
|
-
| `--concurrency <n>` | `30` | Parallel proxy checks. |
|
|
82
|
-
| `--iterations <n>` | `1` | Re-check only successful proxies for `n` rounds. |
|
|
83
|
-
| `--out <prefix>` | `result` | Writes `<prefix>.json` and `<prefix>.txt`. |
|
|
88
|
+
### ๐ Output Files
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
| File | Content |
|
|
91
|
+
|------|---------|
|
|
92
|
+
| `result.json` | Full report: server, port, SNI, latency, error, link |
|
|
93
|
+
| `result.txt` | Working proxy links only, fastest first |
|
|
87
94
|
|
|
88
|
-
## HTTP API
|
|
95
|
+
## ๐ HTTP API Server
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
Basic auth is required.
|
|
97
|
+
Start with **no arguments**:
|
|
92
98
|
|
|
93
99
|
```bash
|
|
94
|
-
TG_API_ID=12345 \
|
|
95
|
-
|
|
96
|
-
CHECK_AUTH_USER=admin \
|
|
97
|
-
CHECK_AUTH_PASSWORD=secret \
|
|
100
|
+
TG_API_ID=12345 TG_API_HASH=abcdef \
|
|
101
|
+
CHECK_AUTH_USER=admin CHECK_AUTH_PASSWORD=secret \
|
|
98
102
|
node check.js
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
POST /check
|
|
105
|
-
Content-Type: application/json
|
|
106
|
-
Authorization: Basic ...
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
"url": "https://example.com/proxies.txt",
|
|
110
|
-
"iterations": 3,
|
|
111
|
-
"concurrency": 10
|
|
112
|
-
}
|
|
105
|
+
```
|
|
106
|
+
[mtproto-checker] โก HTTP server listening on http://localhost:3080
|
|
107
|
+
[mtproto-checker] POST /check (Basic auth: admin:***)
|
|
113
108
|
```
|
|
114
109
|
|
|
115
|
-
|
|
116
|
-
`https://t.me/proxy` link. `iterations` defaults to `1`; `concurrency` defaults
|
|
117
|
-
to `30`.
|
|
118
|
-
|
|
119
|
-
Example:
|
|
110
|
+
### `POST /check`
|
|
120
111
|
|
|
121
112
|
```bash
|
|
122
|
-
curl -u admin:secret \
|
|
123
|
-
-H
|
|
124
|
-
-d '{"url":"
|
|
125
|
-
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}'
|
|
126
116
|
```
|
|
127
117
|
|
|
128
|
-
|
|
118
|
+
**Request body:**
|
|
119
|
+
|
|
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 |
|
|
125
|
+
|
|
126
|
+
**Response:**
|
|
129
127
|
|
|
130
128
|
```json
|
|
131
129
|
{
|
|
132
130
|
"url": "https://example.com/proxies.txt",
|
|
133
|
-
"iterations":
|
|
134
|
-
"concurrency":
|
|
135
|
-
"count":
|
|
136
|
-
"working":
|
|
131
|
+
"iterations": 2,
|
|
132
|
+
"concurrency": 20,
|
|
133
|
+
"count": 150,
|
|
134
|
+
"working": 42,
|
|
137
135
|
"results": [
|
|
138
|
-
{
|
|
139
|
-
"server": "1.2.3.4",
|
|
140
|
-
"port": 443,
|
|
141
|
-
"sni": "example.com",
|
|
142
|
-
"ok": true,
|
|
143
|
-
"ms": 841,
|
|
144
|
-
"error": null,
|
|
145
|
-
"link": "tg://proxy?server=1.2.3.4&port=443&secret=..."
|
|
146
|
-
}
|
|
136
|
+
{ "server": "1.2.3.4", "port": 443, "sni": "example.com", "ok": true, "ms": 312, "error": null, "link": "tg://proxy?..." }
|
|
147
137
|
]
|
|
148
138
|
}
|
|
149
139
|
```
|
|
150
140
|
|
|
151
|
-
|
|
141
|
+
**Error codes:** `400` bad request ยท `401` unauthorized ยท `404` wrong endpoint ยท `405` wrong method ยท `502` upstream fetch failed
|
|
142
|
+
|
|
143
|
+
## ๐ Library API
|
|
152
144
|
|
|
153
145
|
```js
|
|
154
|
-
const {
|
|
146
|
+
const { checkProxyLink, checkProxiesFromURIs, startServer } = require('mtproto-checker')
|
|
147
|
+
```
|
|
155
148
|
|
|
156
|
-
|
|
157
|
-
apiId: Number(process.env.TG_API_ID),
|
|
158
|
-
apiHash: process.env.TG_API_HASH,
|
|
159
|
-
concurrency: 30,
|
|
160
|
-
iterations: 2
|
|
161
|
-
}
|
|
149
|
+
### `checkProxyLink(link, opts)` โ `Promise<Array>`
|
|
162
150
|
|
|
163
|
-
|
|
164
|
-
const fromLists = await checkProxiesFromUrls(['https://example.com/proxies.txt'], opts)
|
|
151
|
+
Check a single `tg://proxy` or `https://t.me/proxy` link.
|
|
165
152
|
|
|
166
|
-
|
|
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
|
+
)
|
|
167
158
|
```
|
|
168
159
|
|
|
169
|
-
|
|
160
|
+
```
|
|
161
|
+
[mtproto-checker] Checking 1.2.3.4:443 [example.com]...
|
|
162
|
+
[mtproto-checker] โ 312ms
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `checkProxiesFromURIs(uris, opts)` โ `Promise<Array>`
|
|
166
|
+
|
|
167
|
+
Check proxies from remote URLs, local files, or both. Auto-detects type per entry.
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
// Remote
|
|
171
|
+
const results = await checkProxiesFromURIs(
|
|
172
|
+
'https://example.com/proxies.txt',
|
|
173
|
+
{ apiId: 12345, apiHash: 'abcdef' }
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// Local
|
|
177
|
+
const results = await checkProxiesFromURIs('./proxies.txt', opts)
|
|
178
|
+
|
|
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 })
|
|
185
|
+
```
|
|
186
|
+
|
|
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)...
|
|
193
|
+
|
|
194
|
+
[ 1/150] โ 312ms 1.2.3.4:443 [example.com]
|
|
195
|
+
[ 2/150] โ Timeout 5.6.7.8:443
|
|
196
|
+
...
|
|
170
197
|
|
|
171
|
-
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
198
|
+
[mtproto-checker] Done: 42/150 working.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `startServer(opts)` โ `Promise<http.Server>`
|
|
202
|
+
|
|
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
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
All fields are optional โ falls back to env vars if omitted.
|
|
216
|
+
|
|
217
|
+
### `opts` Reference
|
|
179
218
|
|
|
180
|
-
|
|
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` |
|
|
181
228
|
|
|
182
|
-
|
|
229
|
+
## ๐งฉ Proxy Link Formats
|
|
183
230
|
|
|
184
|
-
|
|
185
|
-
|
|
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...
|
|
234
|
+
```
|
|
186
235
|
|
|
187
|
-
|
|
236
|
+
Secrets: hex (`ee...`, `dd...`), plain hex, or base64url โ auto-detected. `tg://socks` links are ignored.
|
|
188
237
|
|
|
189
|
-
## Troubleshooting
|
|
238
|
+
## ๐ Troubleshooting
|
|
190
239
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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 |
|
|
195
246
|
|
|
196
|
-
## License
|
|
247
|
+
## ๐ License
|
|
197
248
|
|
|
198
|
-
|
|
249
|
+
ISC
|
package/check.js
CHANGED
|
@@ -415,6 +415,92 @@ 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 directProxies = []
|
|
439
|
+
const texts = await Promise.all(list.map(uri => {
|
|
440
|
+
const parsed = parseLink(uri)
|
|
441
|
+
if (parsed) {
|
|
442
|
+
console.error(` โก ${parsed.server}:${parsed.port}`)
|
|
443
|
+
directProxies.push(parsed)
|
|
444
|
+
return Promise.resolve('')
|
|
445
|
+
}
|
|
446
|
+
if (/^https?:\/\//i.test(uri)) {
|
|
447
|
+
console.error(` โ ${uri}`)
|
|
448
|
+
return fetchText(uri).catch(err => { console.error(` โ Skipping ${uri}: ${err.message}`); return '' })
|
|
449
|
+
}
|
|
450
|
+
console.error(` โ ${uri}`)
|
|
451
|
+
return Promise.resolve(fs.readFileSync(uri, 'utf8'))
|
|
452
|
+
}))
|
|
453
|
+
const fromTexts = mergeProxies(texts)
|
|
454
|
+
const seen = new Set(fromTexts.map(p => `${p.server}:${p.port}:${p.secret}`))
|
|
455
|
+
const proxies = [...fromTexts]
|
|
456
|
+
for (const p of directProxies) {
|
|
457
|
+
const key = `${p.server}:${p.port}:${p.secret}`
|
|
458
|
+
if (seen.has(key)) continue
|
|
459
|
+
seen.add(key)
|
|
460
|
+
proxies.push(p)
|
|
461
|
+
}
|
|
462
|
+
if (proxies.length === 0) {
|
|
463
|
+
console.error('[mtproto-checker] No valid proxy links found.')
|
|
464
|
+
return []
|
|
465
|
+
}
|
|
466
|
+
const iterations = opts.iterations ?? 1
|
|
467
|
+
console.error(`[mtproto-checker] Checking ${proxies.length} proxies (dc=${opts.dc ?? 2}, timeout=${opts.timeout ?? 10}s, concurrency=${opts.concurrency ?? 30}, iterations=${iterations})...\n`)
|
|
468
|
+
const results = await runIterativeChecks(proxies, iterations, (batch, iteration) => {
|
|
469
|
+
if (iterations > 1) console.error(`[mtproto-checker] Iteration ${iteration}/${iterations}: ${batch.length} proxies\n`)
|
|
470
|
+
return checkProxies(batch, {
|
|
471
|
+
...opts,
|
|
472
|
+
onProgress: (proxy, res, index, total) => {
|
|
473
|
+
const tag = res.ok ? `โ ${String(res.ms).padStart(5)}ms` : `โ ${res.error}`
|
|
474
|
+
const sni = proxy.sni ? ` [${proxy.sni}]` : ''
|
|
475
|
+
console.error(` [${String(index + 1).padStart(3)}/${total}] ${tag} ${proxy.server}:${proxy.port}${sni}`)
|
|
476
|
+
if (opts.onProgress) opts.onProgress(proxy, res, index, total)
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
})
|
|
480
|
+
const working = results.filter(c => c.ok).length
|
|
481
|
+
console.error(`\n[mtproto-checker] Done: ${working}/${proxies.length} working.`)
|
|
482
|
+
return results
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Check a single tg://proxy or t.me/proxy link via real MTProto handshake.
|
|
487
|
+
* @param {string} link - proxy link (tg://proxy?... or https://t.me/proxy?...)
|
|
488
|
+
* @param {{apiId: number, apiHash: string, dc?: number, timeout?: number, iterations?: number, onProgress?: Function}} opts
|
|
489
|
+
* @returns {Promise<Array<{proxy: object, ok: boolean, ms: number, error: string|null}>>}
|
|
490
|
+
* @throws if the link is not a valid MTProto proxy link
|
|
491
|
+
*/
|
|
492
|
+
async function checkProxyLink(link, opts) {
|
|
493
|
+
const proxy = parseLink(link)
|
|
494
|
+
if (!proxy) throw new Error('Invalid proxy link. Expected tg://proxy?... or https://t.me/proxy?...')
|
|
495
|
+
const sni = proxy.sni ? ` [${proxy.sni}]` : ''
|
|
496
|
+
console.error(`[mtproto-checker] Checking ${proxy.server}:${proxy.port}${sni}...`)
|
|
497
|
+
const results = await runIterativeChecks([proxy], opts.iterations ?? 1, batch => checkProxies(batch, opts))
|
|
498
|
+
const r = results[0]
|
|
499
|
+
if (r)
|
|
500
|
+
console.error(`[mtproto-checker] ${r.ok ? `โ ${r.ms}ms` : `โ ${r.error}`}`)
|
|
501
|
+
return results
|
|
502
|
+
}
|
|
503
|
+
|
|
418
504
|
async function loadSingleUrlProxies(url, opts = {}) {
|
|
419
505
|
const directProxy = parseLink(url)
|
|
420
506
|
if (directProxy) return [directProxy]
|
|
@@ -574,9 +660,10 @@ function createServer({ auth, checkUrl, logger = console.error }) {
|
|
|
574
660
|
}
|
|
575
661
|
|
|
576
662
|
const body = await readJsonBody(req)
|
|
577
|
-
|
|
663
|
+
const uris = body.url || body.urls || body.uri || body.uris
|
|
664
|
+
if (!uris || (typeof uris === 'string' && uris.trim() === '') || (Array.isArray(uris) && uris.length === 0)) {
|
|
578
665
|
statusCode = 400
|
|
579
|
-
jsonResponse(res, statusCode, { error: 'Request body must include url' })
|
|
666
|
+
jsonResponse(res, statusCode, { error: 'Request body must include url (string or array)' })
|
|
580
667
|
return
|
|
581
668
|
}
|
|
582
669
|
const iterations = body.iterations === undefined ? 1 : body.iterations
|
|
@@ -592,10 +679,10 @@ function createServer({ auth, checkUrl, logger = console.error }) {
|
|
|
592
679
|
return
|
|
593
680
|
}
|
|
594
681
|
|
|
595
|
-
const
|
|
682
|
+
const input = Array.isArray(uris) ? uris.map(u => u.trim()) : [uris.trim()]
|
|
596
683
|
let results
|
|
597
684
|
try {
|
|
598
|
-
results = await checkUrl(
|
|
685
|
+
results = await checkUrl(input, { iterations, concurrency })
|
|
599
686
|
} catch (err) {
|
|
600
687
|
statusCode = 502
|
|
601
688
|
jsonResponse(res, statusCode, {
|
|
@@ -604,15 +691,14 @@ function createServer({ auth, checkUrl, logger = console.error }) {
|
|
|
604
691
|
})
|
|
605
692
|
return
|
|
606
693
|
}
|
|
607
|
-
const report = toReport(results)
|
|
608
694
|
statusCode = 200
|
|
609
695
|
jsonResponse(res, statusCode, {
|
|
610
|
-
|
|
696
|
+
uris: input,
|
|
611
697
|
iterations,
|
|
612
698
|
concurrency,
|
|
613
|
-
count:
|
|
614
|
-
working:
|
|
615
|
-
results
|
|
699
|
+
count: results.length,
|
|
700
|
+
working: results.filter(item => item.ok).length,
|
|
701
|
+
results
|
|
616
702
|
})
|
|
617
703
|
} catch (err) {
|
|
618
704
|
statusCode = err.statusCode || 500
|
|
@@ -629,20 +715,26 @@ function shouldStartServer(argv) {
|
|
|
629
715
|
return argv.length === 0
|
|
630
716
|
}
|
|
631
717
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
718
|
+
/**
|
|
719
|
+
* Start the HTTP API server for proxy checking.
|
|
720
|
+
* @param {{apiId?: number, apiHash?: string, user?: string, password?: string, port?: number}} opts
|
|
721
|
+
* All fields fall back to environment variables if omitted.
|
|
722
|
+
* @returns {Promise<http.Server>} the listening server instance
|
|
723
|
+
*/
|
|
724
|
+
async function startServer(opts = {}) {
|
|
725
|
+
const apiId = opts.apiId ?? parseInt(process.env.TG_API_ID, 10)
|
|
726
|
+
const apiHash = opts.apiHash ?? process.env.TG_API_HASH
|
|
727
|
+
const user = opts.user ?? process.env.CHECK_AUTH_USER
|
|
728
|
+
const password = opts.password ?? process.env.CHECK_AUTH_PASSWORD
|
|
729
|
+
const port = opts.port ?? parseInt(process.env.PORT || '3080', 10)
|
|
638
730
|
|
|
639
731
|
if (!apiId || !apiHash) throw new Error('Set TG_API_ID and TG_API_HASH (get them at https://my.telegram.org).')
|
|
640
732
|
if (!user || !password) throw new Error('Set CHECK_AUTH_USER and CHECK_AUTH_PASSWORD for HTTP Basic auth.')
|
|
641
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('
|
|
733
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('port must be a valid TCP port (1-65535).')
|
|
642
734
|
|
|
643
735
|
const server = createServer({
|
|
644
736
|
auth: { user, password },
|
|
645
|
-
checkUrl: async (url, requestOpts) =>
|
|
737
|
+
checkUrl: async (url, requestOpts) => checkProxiesFromURIs(url, {
|
|
646
738
|
apiId,
|
|
647
739
|
apiHash,
|
|
648
740
|
iterations: requestOpts.iterations,
|
|
@@ -658,7 +750,8 @@ async function startServer(env = process.env) {
|
|
|
658
750
|
})
|
|
659
751
|
})
|
|
660
752
|
|
|
661
|
-
console.error(`
|
|
753
|
+
console.error(`[mtproto-checker] โก HTTP server listening on http://localhost:${port}`)
|
|
754
|
+
console.error(`[mtproto-checker] POST /check (Basic auth: ${user}:***)`)
|
|
662
755
|
return server
|
|
663
756
|
}
|
|
664
757
|
|
|
@@ -725,7 +818,8 @@ async function main() {
|
|
|
725
818
|
process.exit(0)
|
|
726
819
|
}
|
|
727
820
|
|
|
728
|
-
module.exports = {
|
|
821
|
+
module.exports = { checkProxyLink, checkProxiesFromURIs, startServer }
|
|
822
|
+
module.exports._internals = { checkRequestUrl, checkSingleUrl, configureTdlibOnce, createServer, parseArgs, resolveInputProxies, runIterativeChecks, shouldStartServer, parseLink, normalizeSecret, faketlsSni, mergeProxies, loadProxiesFromFile }
|
|
729
823
|
|
|
730
824
|
if (require.main === module) (shouldStartServer(process.argv.slice(2)) ? startServer() : main()).catch(err => {
|
|
731
825
|
console.error(err)
|
package/package.json
CHANGED
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
|