@erfan_bahramali/tping 1.0.0 → 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.
Files changed (98) hide show
  1. package/README.md +76 -55
  2. package/dist/crypto/x25519.d.ts +10 -0
  3. package/dist/crypto/x25519.d.ts.map +1 -0
  4. package/dist/crypto/x25519.js +51 -0
  5. package/dist/crypto/x25519.js.map +1 -0
  6. package/dist/index.js +82 -39
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/attempt.d.ts +31 -0
  9. package/dist/lib/attempt.d.ts.map +1 -0
  10. package/dist/lib/attempt.js +94 -0
  11. package/dist/lib/attempt.js.map +1 -0
  12. package/dist/lib/checker.d.ts +21 -3
  13. package/dist/lib/checker.d.ts.map +1 -1
  14. package/dist/lib/checker.js +133 -49
  15. package/dist/lib/checker.js.map +1 -1
  16. package/dist/lib/datacenters.d.ts +15 -0
  17. package/dist/lib/datacenters.d.ts.map +1 -0
  18. package/dist/lib/datacenters.js +20 -0
  19. package/dist/lib/datacenters.js.map +1 -0
  20. package/dist/lib/parser.d.ts +18 -3
  21. package/dist/lib/parser.d.ts.map +1 -1
  22. package/dist/lib/parser.js +174 -78
  23. package/dist/lib/parser.js.map +1 -1
  24. package/dist/lib/ping.d.ts +13 -6
  25. package/dist/lib/ping.d.ts.map +1 -1
  26. package/dist/lib/ping.js +44 -44
  27. package/dist/lib/ping.js.map +1 -1
  28. package/dist/mtproto/exchange.d.ts +10 -0
  29. package/dist/mtproto/exchange.d.ts.map +1 -0
  30. package/dist/mtproto/exchange.js +20 -0
  31. package/dist/mtproto/exchange.js.map +1 -0
  32. package/dist/mtproto/fake-tls.d.ts +36 -44
  33. package/dist/mtproto/fake-tls.d.ts.map +1 -1
  34. package/dist/mtproto/fake-tls.js +174 -148
  35. package/dist/mtproto/fake-tls.js.map +1 -1
  36. package/dist/mtproto/messages.d.ts +23 -36
  37. package/dist/mtproto/messages.d.ts.map +1 -1
  38. package/dist/mtproto/messages.js +64 -90
  39. package/dist/mtproto/messages.js.map +1 -1
  40. package/dist/mtproto/obfuscation.d.ts +23 -20
  41. package/dist/mtproto/obfuscation.d.ts.map +1 -1
  42. package/dist/mtproto/obfuscation.js +64 -64
  43. package/dist/mtproto/obfuscation.js.map +1 -1
  44. package/dist/mtproto/secret.d.ts +28 -0
  45. package/dist/mtproto/secret.d.ts.map +1 -0
  46. package/dist/mtproto/secret.js +68 -0
  47. package/dist/mtproto/secret.js.map +1 -0
  48. package/dist/mtproto/transport.d.ts +28 -0
  49. package/dist/mtproto/transport.d.ts.map +1 -0
  50. package/dist/mtproto/transport.js +78 -0
  51. package/dist/mtproto/transport.js.map +1 -0
  52. package/dist/net/http-client.d.ts +37 -0
  53. package/dist/net/http-client.d.ts.map +1 -0
  54. package/dist/net/http-client.js +93 -0
  55. package/dist/net/http-client.js.map +1 -0
  56. package/dist/net/http-proxy.d.ts +26 -0
  57. package/dist/net/http-proxy.d.ts.map +1 -0
  58. package/dist/net/http-proxy.js +71 -0
  59. package/dist/net/http-proxy.js.map +1 -0
  60. package/dist/net/socks5.d.ts +15 -0
  61. package/dist/net/socks5.d.ts.map +1 -0
  62. package/dist/net/socks5.js +112 -0
  63. package/dist/net/socks5.js.map +1 -0
  64. package/dist/net/stream.d.ts +54 -0
  65. package/dist/net/stream.d.ts.map +1 -0
  66. package/dist/net/stream.js +142 -0
  67. package/dist/net/stream.js.map +1 -0
  68. package/dist/net/websocket.d.ts +19 -0
  69. package/dist/net/websocket.d.ts.map +1 -0
  70. package/dist/net/websocket.js +123 -0
  71. package/dist/net/websocket.js.map +1 -0
  72. package/dist/types/index.d.ts +40 -17
  73. package/dist/types/index.d.ts.map +1 -1
  74. package/dist/utils/format.d.ts +7 -14
  75. package/dist/utils/format.d.ts.map +1 -1
  76. package/dist/utils/format.js +101 -87
  77. package/dist/utils/format.js.map +1 -1
  78. package/dist/webproxy/check.d.ts +15 -0
  79. package/dist/webproxy/check.d.ts.map +1 -0
  80. package/dist/webproxy/check.js +423 -0
  81. package/dist/webproxy/check.js.map +1 -0
  82. package/dist/webproxy/frames.d.ts +30 -0
  83. package/dist/webproxy/frames.d.ts.map +1 -0
  84. package/dist/webproxy/frames.js +59 -0
  85. package/dist/webproxy/frames.js.map +1 -0
  86. package/dist/webproxy/link.d.ts +27 -0
  87. package/dist/webproxy/link.d.ts.map +1 -0
  88. package/dist/webproxy/link.js +107 -0
  89. package/dist/webproxy/link.js.map +1 -0
  90. package/package.json +16 -11
  91. package/dist/mtproto/connection.d.ts +0 -59
  92. package/dist/mtproto/connection.d.ts.map +0 -1
  93. package/dist/mtproto/connection.js +0 -198
  94. package/dist/mtproto/connection.js.map +0 -1
  95. package/dist/mtproto/socks5.d.ts +0 -36
  96. package/dist/mtproto/socks5.d.ts.map +0 -1
  97. package/dist/mtproto/socks5.js +0 -148
  98. package/dist/mtproto/socks5.js.map +0 -1
package/README.md CHANGED
@@ -1,18 +1,20 @@
1
- # 🏓 tping
1
+ # 🏓 TPing
2
2
 
3
- **Ping for Telegram Proxies** - Test connectivity to Telegram Proxies, just like `ping` for IP addresses.
3
+ **Ping for Telegram Proxies.** It checks Telegram proxies the way `ping` checks IP addresses.
4
4
 
5
- A simple and clean CLI tool to continuously ping Telegram proxies (MTProto and SOCKS5) and measure latency in real-time using official Telegram API.
5
+ TPing connects through the proxy exactly like the official Telegram apps (Android and Desktop) and only reports success when a Telegram datacenter actually answers. If TPing says a proxy works, Telegram can use it. If it fails, the error says where it broke.
6
6
 
7
7
  ---
8
8
 
9
9
  ## ✨ Features
10
10
 
11
- - 🏓 **Continuous ping mode** - Like standard `ping` command
12
- - 🔄 **Multiple proxies** - Ping simultaneously
13
- - ⚙️ **Configurable** - Custom count and interval
14
- - 📊 **Statistics** - min/avg/max latency, packet loss
15
- - 🚀 **Simple & clean** - Easy to use
11
+ - 🔐 **Every official proxy type**: MTProto with simple, `dd`, and Fake TLS (`ee`) secrets, SOCKS5 with or without login, HTTP (Telegram Desktop), and the new WEB proxy (all four carrier modes)
12
+ - 🤝 **Real handshake**: a `req_pq_multi` request goes through the proxy to the Telegram datacenter and must come back with our nonce
13
+ - 🌐 **Datacenter selection**: test DC 2 (default), any set of DCs, or all five
14
+ - 🏓 **Continuous mode** like `ping`, with count and interval
15
+ - 🔄 **Many proxies at once**: from arguments, a file, or stdin
16
+ - 📊 **Statistics**: loss, min/avg/max/mdev, last error
17
+ - 🔍 **Clear errors**, for example: wrong secret, timeout during the Fake TLS handshake, SOCKS5 authentication failed
16
18
 
17
19
  ---
18
20
 
@@ -22,7 +24,7 @@ A simple and clean CLI tool to continuously ping Telegram proxies (MTProto and S
22
24
  npm install -g @erfan_bahramali/tping
23
25
  ```
24
26
 
25
- Or use directly with npx:
27
+ Or use it directly with npx:
26
28
 
27
29
  ```bash
28
30
  npx @erfan_bahramali/tping <proxy>
@@ -32,27 +34,23 @@ npx @erfan_bahramali/tping <proxy>
32
34
 
33
35
  ## 🚀 Usage
34
36
 
35
- You can pass proxy as text or file path. For multiple proxies, separate by newlines (in text or file).
36
-
37
37
  ```bash
38
38
  # Single proxy
39
- tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=0123456789abcdef"
39
+ tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=ee0123456789abcdef0123456789abcdef7777772e676f6f676c652e636f6d"
40
40
 
41
- # Multiple proxies
42
- tping "https://t.me/proxy?server=proxy1.com&port=443&secret=abc
43
- https://t.me/socks?server=proxy2.com&port=1080"
41
+ # Several proxies (arguments, newlines, or spaces)
42
+ tping "tg://proxy?server=a.example.com&port=443&secret=dd0123456789abcdef0123456789abcdef" \
43
+ "https://t.me/socks?server=b.example.com&port=1080&user=me&pass=secret"
44
44
 
45
- # From file (single or multiple proxies)
45
+ # From a file, or from stdin
46
46
  tping proxies.txt
47
+ cat proxies.txt | tping -
47
48
 
48
- # Ping 5 times
49
- tping -c 5 "https://t.me/socks?server=proxy.example.com&port=1080"
50
-
51
- # Ping unlimited times with custom interval (500ms)
52
- tping -i 500 "https://t.me/socks?server=proxy.example.com&port=1080"
49
+ # 5 pings, 500ms apart, through all five Telegram datacenters
50
+ tping -c 5 -i 500 --dc all proxies.txt
53
51
 
54
- # Ping 10 times with custom interval (500ms)
55
- tping -c 10 -i 500 proxies.txt
52
+ # Show how long each phase took
53
+ tping -V -c 3 proxies.txt
56
54
  ```
57
55
 
58
56
  ---
@@ -63,56 +61,79 @@ tping -c 10 -i 500 proxies.txt
63
61
  |--------|-------------|
64
62
  | `-c, --count <n>` | Number of pings to send (default: unlimited) |
65
63
  | `-i, --interval <ms>` | Interval between pings in milliseconds (default: 1000) |
66
- | `-t, --timeout <ms>` | Timeout for each ping in milliseconds (default: 10000) |
64
+ | `-t, --timeout <ms>` | Timeout for one ping (connect + handshake + reply) in milliseconds (default: 10000) |
65
+ | `-d, --dc <ids>` | Telegram DC(s) to test: `1`–`5`, comma-separated, or `all` (default: 2) |
66
+ | `-6, --ipv6` | Use IPv6 datacenter addresses for SOCKS5/HTTP proxies |
67
+ | `-V, --verbose` | Show the time of each phase: TCP connect, proxy handshake, Telegram round trip |
67
68
  | `-v, --version` | Show version number |
68
69
  | `-h, --help` | Show help |
69
70
 
71
+ **Exit status:** `0` if every proxy answered at least once, `1` if any proxy never answered, `2` on invalid input.
72
+
73
+ ---
74
+
75
+ ## 🔗 Supported proxy formats
76
+
77
+ | Type | Formats |
78
+ |------|---------|
79
+ | MTProto | `https://t.me/proxy?server=…&port=…&secret=…`, also `telegram.me`, `telegram.dog`, `www.`, `http://`, no scheme, `tg://proxy?…`, `tg:proxy?…` |
80
+ | SOCKS5 | `https://t.me/socks?server=…&port=…&user=…&pass=…`, `tg://socks?…`, `socks5://user:pass@host:port` |
81
+ | HTTP | `http://user:pass@host:port` (HTTP proxies are only supported by Telegram Desktop) |
82
+ | WEB | `https://t.me/webproxy?server=host[/path]&secret=…`, `tg://webproxy?…` (always HTTPS on port 443) |
83
+
84
+ MTProto secrets may be hex or base64/base64url, like in the official apps:
85
+
86
+ | Secret | Mode | What TPing does |
87
+ |--------|------|-----------------|
88
+ | 16 bytes | simple | obfuscated intermediate transport |
89
+ | `dd` + 16 bytes | secure | obfuscated padded intermediate transport |
90
+ | `ee` + 16 bytes + domain | Fake TLS | TLS-looking handshake with the domain as SNI, then padded intermediate inside TLS records |
91
+
92
+ WEB proxy secrets are 16 bytes or `dd` + 16 bytes (no Fake TLS). A link whose server has a base path (`host%2Fpath`) carries the secret in its marked form (`0x70` + secret, base64url), as the official installer generates it.
93
+
94
+ Shell-escaped links (`t.me/proxy\?server\=…`) are accepted. Invalid lines are reported and skipped.
95
+
70
96
  ---
71
97
 
72
98
  ## 📊 Example Output
73
99
 
74
100
  ```
75
- 🏓 PING proxy.example.com:443 (MTProto)
76
- Sending pings with 1000ms interval... (Press Ctrl+C to stop)
77
-
78
- ✓ proxy.example.com:443 │ MTProto │ seq=1 │ 45ms
79
- ✓ proxy.example.com:443 │ MTProto │ seq=2 │ 48ms
80
- ✓ proxy.example.com:443 │ MTProto │ seq=3 │ 43ms
81
- ^C
82
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
83
- 📊 Statistics for proxy.example.com:443
84
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
85
- Packets: 3 sent │ 3 received │ 0% loss
86
- Latency: 43ms min │ 45ms avg │ 48ms max
87
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
101
+ ✅ #1 │ [1] proxy.example.com:443 │ MTProto · Fake TLS │ 182ms
102
+ ❌ #1 │ [2] 203.0.113.7:8443 │ MTProto · dd │ Proxy closed the connection before Telegram answered (wrong secret, or the proxy cannot reach Telegram)
103
+ ✅ #1 │ [3] socks.example.com:1080 │ SOCKS5 │ 95ms
88
104
  ```
89
105
 
106
+ Press Ctrl+C to stop and print the statistics table. Press it twice to quit immediately.
107
+
90
108
  ---
91
109
 
92
- ## 🔍 How it Works
110
+ ## 🔍 How it works
111
+
112
+ TPing builds the same connection as the official apps:
113
+
114
+ 1. **Fake TLS (`ee`)**: sends a Chrome-like TLS ClientHello (shuffled extensions, GREASE, a valid x25519 key share, padded to 517 bytes). The client random is an HMAC-SHA256 of the hello with the secret, with the current time mixed in. The proxy's reply must be exactly ServerHello + ChangeCipherSpec + one Application Data record carrying a valid HMAC. A wrong secret, or the real website answering instead of the proxy, is detected here.
115
+ 2. **Transport obfuscation**: a random 64-byte header carries the protocol tag and the DC id, encrypted with AES-256-CTR keys derived from the secret ([MTProto transport obfuscation](https://core.telegram.org/mtproto/mtproto-transports#transport-obfuscation)).
116
+ 3. **MTProto ping**: a `req_pq_multi` request is sent and a `resPQ` with the same nonce must come back from the Telegram datacenter.
117
+
118
+ For **WEB proxies** ([tproxy-server](https://github.com/telegramdesktop/tproxy-server)), TPing does what the Telegram app and its bridge page do together. It derives the bridge capability (HMAC-SHA256 of the hostname, and base path if any, keyed with the secret) and loads `https://host/?bridge=…` with full certificate checks. It then creates a relay session (HELLO → WELCOME) and opens one stream per tested DC, carrying the normal obfuscated MTProxy bytes until the DC answers. All four carrier modes the relay can choose are supported: `https`, `https-lanes`, `websocket` and `websocket-lanes`. The session is deleted afterwards so it does not take up a slot on the relay. The official apps skip WEB proxies in their availability checks; TPing tests them for real.
119
+
120
+ For SOCKS5 and HTTP proxies, TPing opens a tunnel to the Telegram datacenter's IP and runs the same obfuscated MTProto handshake through it. For HTTP proxies that refuse `CONNECT`, it falls back to the HTTP transport, as Telegram Desktop does.
93
121
 
94
- tping performs a **real MTProto handshake** through the proxy — the same first
95
- step every official Telegram client does:
122
+ The reported latency covers the whole sequence, TCP connect + proxy handshake + Telegram round trip, which is the same thing the official apps show as a proxy's ping. With several DCs, the slowest DC is reported and each DC is listed.
96
123
 
97
- 1. **Transport obfuscation** — a 64-byte header is generated with AES-256-CTR
98
- keys derived from the proxy secret (per the official
99
- [MTProto transport obfuscation](https://core.telegram.org/mtproto/mtproto-transports#transport-obfuscation)).
100
- 2. **Fake TLS** (secrets with `ee` prefix) — a TLS 1.3 ClientHello with a valid
101
- HMAC digest is sent; the proxy's signed ServerHello is verified before any
102
- data is trusted. Wrong secrets are detected immediately.
103
- 3. **MTProto ping** — a `req_pq_multi` request is sent through the proxy to the
104
- Telegram datacenter. The measured latency is the round trip of receiving a
105
- valid `resPQ` reply with our echoed nonce. A proxy only passes if the
106
- datacenter actually answers — dead or fake proxies are reported as failures,
107
- never as successes.
124
+ ---
108
125
 
109
- SOCKS5 proxies are checked by negotiating a connection to a Telegram datacenter
110
- through them.
126
+ ## 🧪 Development
127
+
128
+ ```bash
129
+ bun install
130
+ bun test # unit tests + end-to-end tests against local mock servers (MTProxy, Fake TLS, SOCKS5, HTTP, WEB relay)
131
+ bun run build
132
+ TPING_LIVE_PROXY="https://t.me/proxy?server=…&port=…&secret=…" bun test # also test a real proxy
133
+ ```
111
134
 
112
135
  ---
113
136
 
114
137
  ## 📋 License
115
138
 
116
139
  MIT © 2026 [ErfanBahramali](https://github.com/ErfanBahramali)
117
-
118
- ---
@@ -0,0 +1,10 @@
1
+ export declare const isQuadraticResidue: (value: bigint) => boolean;
2
+ export declare const bufferToBigInt: (buffer: Buffer) => bigint;
3
+ /**
4
+ * Generate a public key that is a valid x25519 point, as Telegram clients do
5
+ * for the fake TLS key_share extension (a random 32-byte string would be an
6
+ * easy DPI fingerprint, since half of them are not on the curve).
7
+ */
8
+ export declare const generateX25519PublicKey: () => Buffer;
9
+ export declare const isOnCurve: (key: Buffer) => boolean;
10
+ //# sourceMappingURL=x25519.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x25519.d.ts","sourceRoot":"","sources":["../../src/crypto/x25519.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,OACE,CAAC;AAStD,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,MAAsD,CAAC;AAEvG;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QAAO,MAa1C,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,OAAgE,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { randomBytes } from 'crypto';
2
+ const P = (1n << 255n) - 19n;
3
+ const A = 486662n;
4
+ const mod = (value) => {
5
+ const result = value % P;
6
+ return result < 0n ? result + P : result;
7
+ };
8
+ const modPow = (base, exponent) => {
9
+ let result = 1n;
10
+ let b = mod(base);
11
+ let e = exponent;
12
+ while (e > 0n) {
13
+ if (e & 1n) {
14
+ result = (result * b) % P;
15
+ }
16
+ b = (b * b) % P;
17
+ e >>= 1n;
18
+ }
19
+ return result;
20
+ };
21
+ /** y^2 = x^3 + A*x^2 + x on Curve25519. */
22
+ const curveY2 = (x) => mod(((x + A) * x + 1n) * x);
23
+ export const isQuadraticResidue = (value) => value === 0n || modPow(value, (P - 1n) / 2n) === 1n;
24
+ /** x-coordinate of 2*point (Montgomery doubling). */
25
+ const doubleX = (x) => {
26
+ const numerator = mod(x * x - 1n);
27
+ const denominator = mod(4n * curveY2(x));
28
+ return mod(numerator * numerator * modPow(denominator, P - 2n));
29
+ };
30
+ export const bufferToBigInt = (buffer) => BigInt(`0x${buffer.toString('hex') || '0'}`);
31
+ /**
32
+ * Generate a public key that is a valid x25519 point, as Telegram clients do
33
+ * for the fake TLS key_share extension (a random 32-byte string would be an
34
+ * easy DPI fingerprint, since half of them are not on the curve).
35
+ */
36
+ export const generateX25519PublicKey = () => {
37
+ for (;;) {
38
+ const candidate = randomBytes(32);
39
+ candidate[31] &= 0x7f;
40
+ let x = mod(bufferToBigInt(candidate));
41
+ if (!isQuadraticResidue(curveY2(x))) {
42
+ continue;
43
+ }
44
+ for (let i = 0; i < 3; i++) {
45
+ x = doubleX(x);
46
+ }
47
+ return Buffer.from(x.toString(16).padStart(64, '0'), 'hex');
48
+ }
49
+ };
50
+ export const isOnCurve = (key) => isQuadraticResidue(curveY2(mod(bufferToBigInt(key))));
51
+ //# sourceMappingURL=x25519.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x25519.js","sourceRoot":"","sources":["../../src/crypto/x25519.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC;AAC7B,MAAM,CAAC,GAAG,OAAO,CAAC;AAElB,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE;IACpC,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;IACzB,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAU,EAAE;IACxD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,IAAI,CAAC,GAAG,QAAQ,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACX,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC,KAAK,EAAE,CAAC;IACX,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,2CAA2C;AAC3C,MAAM,OAAO,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAW,EAAE,CAC3D,KAAK,KAAK,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;AAEtD,qDAAqD;AACrD,MAAM,OAAO,GAAG,CAAC,CAAS,EAAU,EAAE;IACpC,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAClC,MAAM,WAAW,GAAG,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,OAAO,GAAG,CAAC,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AAEvG;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAW,EAAE;IAClD,SAAS,CAAC;QACR,MAAM,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAClC,SAAS,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;QACtB,IAAI,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,SAAS;QACX,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -1,66 +1,109 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'module';
2
3
  import chalk from 'chalk';
3
- import { program } from 'commander';
4
+ import { InvalidArgumentError, Option, program } from 'commander';
4
5
  import { parseProxies } from './lib/parser.js';
5
- import { pingProxy } from './lib/ping.js';
6
+ import { pingProxies } from './lib/ping.js';
7
+ import { DC_IDS, DEFAULT_DC } from './lib/datacenters.js';
8
+ import { waitForWebProxyCleanup } from './webproxy/check.js';
9
+ const { version } = createRequire(import.meta.url)('../package.json');
10
+ const integer = (name, min) => (value) => {
11
+ const number = Number(value);
12
+ if (!/^\d+$/.test(value.trim()) || !Number.isSafeInteger(number) || number < min) {
13
+ throw new InvalidArgumentError(`${name} must be an integer >= ${min}.`);
14
+ }
15
+ return number;
16
+ };
17
+ const parseDcs = (value) => {
18
+ if (value.trim().toLowerCase() === 'all') {
19
+ return [...DC_IDS];
20
+ }
21
+ const ids = value.split(',').map((part) => Number(part.trim()));
22
+ if (ids.length === 0 || ids.some((id) => !DC_IDS.includes(id))) {
23
+ throw new InvalidArgumentError(`DC must be "all" or a comma-separated list of ${DC_IDS.join(', ')}.`);
24
+ }
25
+ return [...new Set(ids)];
26
+ };
6
27
  program
7
28
  .name('tping')
8
29
  .description('Ping for Telegram Proxies')
9
- .version('1.0.0', '-v, --version')
10
- .argument('[proxy]', 'Proxy URL or file path (supports MTProto, SOCKS5)')
11
- .option('-c, --count <n>', 'Number of pings to send (default: unlimited)', parseInt)
12
- .option('-i, --interval <ms>', 'Interval between pings in milliseconds (default: 1000)', parseInt)
13
- .option('-t, --timeout <ms>', 'Timeout for each ping in milliseconds (default: 10000)', parseInt)
30
+ .version(version, '-v, --version')
31
+ .argument('[proxies...]', 'Proxy links, file paths, or "-" for stdin')
32
+ .option('-c, --count <n>', 'Number of pings to send (default: unlimited)', integer('count', 1))
33
+ .option('-i, --interval <ms>', 'Interval between pings in milliseconds', integer('interval', 0), 1000)
34
+ .option('-t, --timeout <ms>', 'Timeout for each ping (connect + handshake + reply) in milliseconds', integer('timeout', 100), 10000)
35
+ .addOption(new Option('-d, --dc <ids>', 'Telegram DC(s) to test through the proxy: 1-5, comma-separated, or "all"')
36
+ .argParser(parseDcs)
37
+ .default([DEFAULT_DC], String(DEFAULT_DC)))
38
+ .option('-6, --ipv6', 'Use IPv6 DC addresses for SOCKS5/HTTP proxies', false)
39
+ .option('-V, --verbose', 'Show the time spent in each phase (tcp, handshake, rtt)', false)
14
40
  .showHelpAfterError('(add --help for additional information)')
15
41
  .addHelpText('after', `
16
42
  Supported proxy formats:
17
- MTProto: "https://t.me/proxy?server=...&port=...&secret=..."
18
- MTProto: "tg://proxy?server=...&port=...&secret=..."
19
- SOCKS5: "https://t.me/socks?server=...&port=...&user=...&pass=..."
20
- SOCKS5: "tg://socks?server=...&port=...&user=...&pass=..."
43
+ MTProto: https://t.me/proxy?server=...&port=...&secret=... (also tg://proxy?...)
44
+ secret: 32 hex (simple), dd + 32 hex (secure), ee + 32 hex + domain hex (Fake TLS),
45
+ or the same bytes in base64 / base64url
46
+ SOCKS5: https://t.me/socks?server=...&port=...&user=...&pass=... (also tg://socks?...)
47
+ socks5://user:pass@host:port
48
+ HTTP: http://user:pass@host:port (Telegram Desktop only)
49
+ WEB: https://t.me/webproxy?server=host[/path]&secret=... (also tg://webproxy?...)
50
+ HTTPS on port 443 through a tproxy-server relay; secret: 32 hex or dd + 32 hex
51
+
52
+ A ping succeeds only when a Telegram datacenter answers through the proxy.
53
+ Exit status: 0 if every proxy answered at least once, 1 otherwise, 2 on bad input.
21
54
 
22
55
  Examples:
23
- $ tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=0123456789abcdef"
56
+ $ tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=0123456789abcdef0123456789abcdef"
24
57
  $ tping -c 5 "https://t.me/socks?server=proxy.example.com&port=1080"
25
- $ tping -c 10 -i 500 "tg://socks?server=proxy.example.com&port=1080&user=admin&pass=secret"
26
- $ tping proxies.txt
58
+ $ tping -c 10 -i 500 --dc all proxies.txt
59
+ $ cat proxies.txt | tping -c 1 -
27
60
  `)
28
- .action(async (proxyInput, options) => {
29
- if (!proxyInput) {
61
+ .action(async (inputs, options) => {
62
+ if (inputs.length === 0) {
30
63
  program.help();
31
64
  }
32
- // Parse proxies
33
- const proxies = parseProxies(proxyInput);
65
+ const parsed = inputs.map(parseProxies);
66
+ const proxies = parsed.flatMap((p) => p.proxies);
67
+ const issues = parsed.flatMap((p) => p.issues);
68
+ for (const issue of issues) {
69
+ const shown = issue.input.length > 60 ? `${issue.input.slice(0, 57)}...` : issue.input;
70
+ console.error(chalk.yellow(`⚠️ Skipped "${shown}": ${issue.reason}`));
71
+ }
34
72
  if (proxies.length === 0) {
35
- console.log(chalk.red('Error: Invalid proxy format'));
36
- process.exit(1);
73
+ console.error(chalk.red('Error: no valid proxy found (pass a proxy link or a file path)'));
74
+ process.exit(2);
37
75
  }
38
- options.interval = options.interval || 1000;
39
- options.timeout = options.timeout || 10000;
40
- // Print header
41
76
  console.log();
42
77
  console.log(chalk.bold.cyan('🏓 TPing'));
43
78
  console.log(chalk.gray('━'.repeat(50)));
44
- console.log(chalk.cyan(`📡 Proxies: ${chalk.white(proxies.length)}`));
45
- console.log(chalk.cyan(`🎯 Count: ${chalk.white(options.count || 'unlimited')}`));
46
- console.log(chalk.cyan(`⏰ Interval: ${chalk.white(options.interval + 'ms')}`));
47
- console.log(chalk.cyan(`⏳ Timeout: ${chalk.white(options.timeout + 'ms')}`));
79
+ console.log(chalk.cyan(`📡 Proxies: ${chalk.white(proxies.length)}`));
80
+ console.log(chalk.cyan(`🎯 Count: ${chalk.white(options.count ?? 'unlimited')}`));
81
+ console.log(chalk.cyan(`⏰ Interval: ${chalk.white(`${options.interval}ms`)}`));
82
+ console.log(chalk.cyan(`⏳ Timeout: ${chalk.white(`${options.timeout}ms`)}`));
83
+ console.log(chalk.cyan(`🌐 DC: ${chalk.white(options.dc.join(', '))}`));
48
84
  console.log(chalk.gray('━'.repeat(50)));
49
85
  console.log();
50
- // Setup abort controller for Ctrl+C
51
86
  const abortController = new AbortController();
52
- let isAborting = false;
53
- const handleAbort = async () => {
54
- if (isAborting) {
55
- return;
87
+ const handleSignal = () => {
88
+ if (abortController.signal.aborted) {
89
+ process.exit(130);
56
90
  }
57
- isAborting = true;
58
91
  abortController.abort();
59
- console.log(chalk.yellow('\n⏸️ Stopping... (Press Ctrl+C again to force quit)'));
92
+ process.stderr.write(chalk.yellow('\n⏸️ Stopping... (press Ctrl+C again to force quit)\n'));
60
93
  };
61
- process.on('SIGINT', handleAbort);
62
- process.on('SIGTERM', handleAbort);
63
- await pingProxy(proxies, options, abortController.signal);
64
- process.exit(0);
65
- }).parse();
94
+ process.on('SIGINT', handleSignal);
95
+ process.on('SIGTERM', handleSignal);
96
+ const results = await pingProxies(proxies, {
97
+ count: options.count,
98
+ interval: options.interval,
99
+ timeout: options.timeout,
100
+ dcs: options.dc,
101
+ ipv6: options.ipv6,
102
+ verbose: options.verbose,
103
+ }, abortController.signal);
104
+ const allAnswered = results.every((list) => list.some((r) => r.success));
105
+ await waitForWebProxyCleanup();
106
+ process.exit(allAnswered ? 0 : 1);
107
+ });
108
+ await program.parseAsync();
66
109
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Socket } from 'net';
2
+ import { SocketStream } from '../net/stream.js';
3
+ export declare class TimeoutError extends Error {
4
+ constructor();
5
+ }
6
+ export declare class AbortedError extends Error {
7
+ constructor();
8
+ }
9
+ export declare const STAGE_CONNECTING = "connecting to proxy";
10
+ export declare const STAGE_WAITING = "waiting for Telegram";
11
+ /**
12
+ * One check: a deadline and an abort signal shared by every connection it opens,
13
+ * plus the current phase for error messages.
14
+ */
15
+ export declare class Attempt {
16
+ private readonly signal?;
17
+ stage: string;
18
+ private readonly streams;
19
+ private primary?;
20
+ private failure?;
21
+ private readonly timer;
22
+ private readonly onAbort;
23
+ constructor(deadline: number, signal?: AbortSignal | undefined);
24
+ /** The default TCP stream, created on first use. */
25
+ get stream(): SocketStream;
26
+ newStream(socket?: Socket, readyEvent?: string): SocketStream;
27
+ close(): void;
28
+ private failAll;
29
+ }
30
+ export declare const describeError: (err: unknown, stage: string) => string;
31
+ //# sourceMappingURL=attempt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt.d.ts","sourceRoot":"","sources":["../../src/lib/attempt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,YAAY,EAAqB,MAAM,kBAAkB,CAAC;AAEnE,qBAAa,YAAa,SAAQ,KAAK;;CAKtC;AAED,qBAAa,YAAa,SAAQ,KAAK;;CAKtC;AAED,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AACtD,eAAO,MAAM,aAAa,yBAAyB,CAAC;AAEpD;;;GAGG;AACH,qBAAa,OAAO;IAQY,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAPtD,KAAK,SAAoB;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgD;gBAE5D,QAAQ,EAAE,MAAM,EAAmB,MAAM,CAAC,EAAE,WAAW,YAAA;IAQnE,oDAAoD;IACpD,IAAI,MAAM,IAAI,YAAY,CAGzB;IAED,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY;IAS7D,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,OAAO;CAIhB;AAeD,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,OAAO,MAAM,KAAG,MAuB3D,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { SocketStream, StreamClosedError } from '../net/stream.js';
2
+ export class TimeoutError extends Error {
3
+ constructor() {
4
+ super('Timeout');
5
+ this.name = 'TimeoutError';
6
+ }
7
+ }
8
+ export class AbortedError extends Error {
9
+ constructor() {
10
+ super('Aborted');
11
+ this.name = 'AbortedError';
12
+ }
13
+ }
14
+ export const STAGE_CONNECTING = 'connecting to proxy';
15
+ export const STAGE_WAITING = 'waiting for Telegram';
16
+ /**
17
+ * One check: a deadline and an abort signal shared by every connection it opens,
18
+ * plus the current phase for error messages.
19
+ */
20
+ export class Attempt {
21
+ signal;
22
+ stage = STAGE_CONNECTING;
23
+ streams = new Set();
24
+ primary;
25
+ failure;
26
+ timer;
27
+ onAbort = () => this.failAll(new AbortedError());
28
+ constructor(deadline, signal) {
29
+ this.signal = signal;
30
+ this.timer = setTimeout(() => this.failAll(new TimeoutError()), Math.max(0, deadline - Date.now()));
31
+ if (signal?.aborted) {
32
+ this.onAbort();
33
+ }
34
+ signal?.addEventListener('abort', this.onAbort, { once: true });
35
+ }
36
+ /** The default TCP stream, created on first use. */
37
+ get stream() {
38
+ this.primary ??= this.newStream();
39
+ return this.primary;
40
+ }
41
+ newStream(socket, readyEvent) {
42
+ const stream = new SocketStream(socket, readyEvent);
43
+ if (this.failure) {
44
+ stream.fail(this.failure);
45
+ }
46
+ this.streams.add(stream);
47
+ return stream;
48
+ }
49
+ close() {
50
+ clearTimeout(this.timer);
51
+ this.signal?.removeEventListener('abort', this.onAbort);
52
+ this.streams.forEach((stream) => stream.destroy());
53
+ }
54
+ failAll(err) {
55
+ this.failure ??= err;
56
+ this.streams.forEach((stream) => stream.fail(err));
57
+ }
58
+ }
59
+ const NETWORK_ERRORS = {
60
+ ECONNREFUSED: 'Connection refused',
61
+ ENOTFOUND: 'DNS lookup failed (unknown host)',
62
+ EAI_AGAIN: 'DNS lookup failed (temporary)',
63
+ EHOSTUNREACH: 'Host unreachable',
64
+ ENETUNREACH: 'Network unreachable',
65
+ ETIMEDOUT: 'Connection timed out',
66
+ EPIPE: 'Connection broken',
67
+ };
68
+ const CERTIFICATE_ERROR = /CERT|SELF_SIGNED|ALTNAME|UNABLE_TO_(GET|VERIFY)/;
69
+ const TLS_HANDSHAKE_ERROR = /^ERR_(SSL|TLS)_/;
70
+ export const describeError = (err, stage) => {
71
+ if (err instanceof TimeoutError) {
72
+ return `Timeout while ${stage}`;
73
+ }
74
+ if (err instanceof StreamClosedError) {
75
+ return stage === STAGE_WAITING
76
+ ? 'Proxy closed the connection before Telegram answered (wrong secret, or the proxy cannot reach Telegram)'
77
+ : `Connection closed while ${stage}`;
78
+ }
79
+ const code = err?.code;
80
+ if (code === 'ECONNRESET') {
81
+ return `Connection reset while ${stage}`;
82
+ }
83
+ if (code && NETWORK_ERRORS[code]) {
84
+ return NETWORK_ERRORS[code];
85
+ }
86
+ if (code && CERTIFICATE_ERROR.test(code)) {
87
+ return `TLS certificate error (${code})`;
88
+ }
89
+ if (code && TLS_HANDSHAKE_ERROR.test(code)) {
90
+ return `TLS handshake failed, not a normal HTTPS server (${code})`;
91
+ }
92
+ return err instanceof Error ? err.message : String(err);
93
+ };
94
+ //# sourceMappingURL=attempt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt.js","sourceRoot":"","sources":["../../src/lib/attempt.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,24 @@
1
- import type { TelegramProxy, CheckResult } from '../types/index.js';
1
+ import type { CheckResult, TelegramProxy } from '../types/index.js';
2
+ import { type DcAddress } from './datacenters.js';
3
+ export interface CheckOptions {
4
+ /** Deadline for one whole check (connect + handshake + round trip), per DC. */
5
+ readonly timeout: number;
6
+ readonly dcs?: readonly number[];
7
+ /** Use IPv6 DC addresses for SOCKS5/HTTP proxies. */
8
+ readonly ipv6?: boolean;
9
+ readonly signal?: AbortSignal;
10
+ /** Override DC addresses (tests). */
11
+ readonly resolveDc?: (dc: number, transport: 'tcp' | 'http') => DcAddress;
12
+ /** Override where a WEB proxy hostname connects, and trusted CA (tests). */
13
+ readonly webProxyEndpoint?: {
14
+ readonly host: string;
15
+ readonly port: number;
16
+ readonly ca?: Buffer;
17
+ };
18
+ }
2
19
  /**
3
- * Ping any proxy type.
20
+ * Check one proxy the way the official clients use it: open a real connection,
21
+ * do the proxy handshake, and get a resPQ back from each requested Telegram DC.
4
22
  */
5
- export declare const checkProxy: (proxy: TelegramProxy, timeout?: number) => Promise<CheckResult>;
23
+ export declare const checkProxy: (proxy: TelegramProxy, options: CheckOptions) => Promise<CheckResult>;
6
24
  //# sourceMappingURL=checker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../../src/lib/checker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0EpE;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,aAAa,EACpB,UAAS,MAAc,KACtB,OAAO,CAAC,WAAW,CAQrB,CAAC"}
1
+ {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../../src/lib/checker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAKX,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,qDAAqD;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,MAAM,KAAK,SAAS,CAAC;IAC1E,4EAA4E;IAC5E,QAAQ,CAAC,gBAAgB,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpG;AAgID;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAU,OAAO,aAAa,EAAE,SAAS,YAAY,KAAG,OAAO,CAAC,WAAW,CA+BjG,CAAC"}