v2ray-finder 0.2.1__tar.gz
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.
- v2ray_finder-0.2.1/CHANGELOG.md +177 -0
- v2ray_finder-0.2.1/LICENSE +22 -0
- v2ray_finder-0.2.1/MANIFEST.in +8 -0
- v2ray_finder-0.2.1/PKG-INFO +405 -0
- v2ray_finder-0.2.1/README.md +350 -0
- v2ray_finder-0.2.1/pyproject.toml +104 -0
- v2ray_finder-0.2.1/setup.cfg +4 -0
- v2ray_finder-0.2.1/src/v2ray_finder/__init__.py +55 -0
- v2ray_finder-0.2.1/src/v2ray_finder/async_fetcher.py +500 -0
- v2ray_finder-0.2.1/src/v2ray_finder/cache.py +395 -0
- v2ray_finder-0.2.1/src/v2ray_finder/cli.py +563 -0
- v2ray_finder-0.2.1/src/v2ray_finder/cli_rich.py +618 -0
- v2ray_finder-0.2.1/src/v2ray_finder/core.py +959 -0
- v2ray_finder-0.2.1/src/v2ray_finder/exceptions.py +188 -0
- v2ray_finder-0.2.1/src/v2ray_finder/gui/__init__.py +1 -0
- v2ray_finder-0.2.1/src/v2ray_finder/gui/main_window.py +314 -0
- v2ray_finder-0.2.1/src/v2ray_finder/health_checker.py +437 -0
- v2ray_finder-0.2.1/src/v2ray_finder/result.py +65 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/PKG-INFO +405 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/SOURCES.txt +38 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/dependency_links.txt +1 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/entry_points.txt +4 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/requires.txt +33 -0
- v2ray_finder-0.2.1/src/v2ray_finder.egg-info/top_level.txt +1 -0
- v2ray_finder-0.2.1/tests/test_async_fetcher.py +484 -0
- v2ray_finder-0.2.1/tests/test_cache.py +451 -0
- v2ray_finder-0.2.1/tests/test_cli.py +412 -0
- v2ray_finder-0.2.1/tests/test_cli_rich.py +388 -0
- v2ray_finder-0.2.1/tests/test_core.py +299 -0
- v2ray_finder-0.2.1/tests/test_error_handling.py +199 -0
- v2ray_finder-0.2.1/tests/test_exceptions.py +145 -0
- v2ray_finder-0.2.1/tests/test_get_repo_files.py +171 -0
- v2ray_finder-0.2.1/tests/test_health_basic.py +141 -0
- v2ray_finder-0.2.1/tests/test_health_checker.py +579 -0
- v2ray_finder-0.2.1/tests/test_imports.py +53 -0
- v2ray_finder-0.2.1/tests/test_integration.py +113 -0
- v2ray_finder-0.2.1/tests/test_result.py +111 -0
- v2ray_finder-0.2.1/tests/test_servers_from_sources.py +206 -0
- v2ray_finder-0.2.1/tests/test_servers_with_health.py +195 -0
- v2ray_finder-0.2.1/tests/test_stop_mechanism.py +609 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to v2ray-finder will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
|
|
7
|
+
## [0.2.1] - 2026-02-24
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Graceful stop / Ctrl+C handling** — complete overhaul across all layers:
|
|
12
|
+
|
|
13
|
+
**`core.py` — `get_servers_from_known_sources()`**
|
|
14
|
+
- Added `try/except KeyboardInterrupt` around each URL fetch
|
|
15
|
+
- On interrupt: calls `self.request_stop()`, breaks the loop, returns
|
|
16
|
+
whatever was already collected (partial results are no longer lost)
|
|
17
|
+
|
|
18
|
+
**`core.py` — `get_servers_from_github()`**
|
|
19
|
+
- Same `try/except KeyboardInterrupt` pattern applied to the repo-file
|
|
20
|
+
iteration loop
|
|
21
|
+
- Partial results from files fetched before Ctrl+C are preserved
|
|
22
|
+
|
|
23
|
+
**`core.py` — `get_servers_with_health()`**
|
|
24
|
+
- Added `health_batch_size` parameter (default `50`) to split the
|
|
25
|
+
server list into smaller batches
|
|
26
|
+
- `should_stop()` is checked between every batch — no longer necessary
|
|
27
|
+
to wait for all health checks to finish before the stop takes effect
|
|
28
|
+
- `try/except KeyboardInterrupt` inside the batch loop: already-checked
|
|
29
|
+
servers are returned immediately on interrupt
|
|
30
|
+
|
|
31
|
+
**`cli.py` — interactive menu**
|
|
32
|
+
- Removed `start_keyboard_listener()` / `stop_keyboard_listener()` calls
|
|
33
|
+
from `interactive_menu()` — the background thread competed with the
|
|
34
|
+
menu's own `input()` calls, silently discarding the first keystroke
|
|
35
|
+
after each operation
|
|
36
|
+
- Every menu operation (options 1–5) now has its own
|
|
37
|
+
`try/except KeyboardInterrupt` with partial-results save
|
|
38
|
+
- Replaced bare boolean `_stop_listener` flag with `threading.Event`
|
|
39
|
+
inside `StopController` for thread-safe lifecycle control
|
|
40
|
+
- `StopController` (listener thread) is now used **only** in the
|
|
41
|
+
non-interactive (`--output` / `--stats-only`) path where the main
|
|
42
|
+
thread never calls `input()` during a fetch
|
|
43
|
+
|
|
44
|
+
**`cli_rich.py` — Rich interactive mode**
|
|
45
|
+
- `_signal_handler()` now calls `_active_finder.request_stop()` before
|
|
46
|
+
re-raising `KeyboardInterrupt`; previously it only set a bare boolean
|
|
47
|
+
that the core loops never checked
|
|
48
|
+
- `fetch_servers()` updates the `partial` snapshot after every
|
|
49
|
+
individual step (known sources, then GitHub search) so a Ctrl+C at
|
|
50
|
+
any point yields whatever was collected up to that moment
|
|
51
|
+
- `StopController` (same pattern as plain CLI) used only in
|
|
52
|
+
non-interactive path to avoid competing `input()` threads
|
|
53
|
+
|
|
54
|
+
### Tests
|
|
55
|
+
|
|
56
|
+
- Added `TestHealthBatchStop` class to `tests/test_stop_mechanism.py`:
|
|
57
|
+
- `test_ki_during_batch_returns_partial` — KI on batch N returns
|
|
58
|
+
results from batches 1…N-1 and sets `should_stop()`
|
|
59
|
+
- `test_ki_does_not_propagate` — `KeyboardInterrupt` must not escape
|
|
60
|
+
`get_servers_with_health()`
|
|
61
|
+
- `test_should_stop_between_batches_stops_processing` — once
|
|
62
|
+
`should_stop()` is `True`, no further `check_servers()` calls are made
|
|
63
|
+
- `test_custom_batch_size_splits_work` — `health_batch_size=2` with 6
|
|
64
|
+
servers results in exactly 3 `check_servers()` calls
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## [0.2.0] - 2026-02-20
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **Async HTTP Fetching** (`async_fetcher` module)
|
|
73
|
+
- 10-50x faster concurrent downloads via `asyncio`
|
|
74
|
+
- Multiple backends: aiohttp (preferred), httpx, or sync fallback
|
|
75
|
+
- Connection pooling and per-request timeout control
|
|
76
|
+
- Automatic retry with exponential backoff (configurable `max_retries`)
|
|
77
|
+
- `fetch_urls_concurrently()` convenience function
|
|
78
|
+
|
|
79
|
+
- **Smart Caching Layer** (`cache` module)
|
|
80
|
+
- Memory cache (fast, temporary) and disk cache (persistent via `diskcache`)
|
|
81
|
+
- Configurable TTL per entry
|
|
82
|
+
- Cache statistics: hit rate, hits, misses
|
|
83
|
+
- `@cache.cached('key', ttl=3600)` decorator support
|
|
84
|
+
- Automatic expiration and cleanup
|
|
85
|
+
|
|
86
|
+
- **Enhanced Error Handling** (`exceptions` + `result` modules)
|
|
87
|
+
- `Result[T, E]` type for explicit error handling (`.is_ok()`, `.unwrap()`, `.error`)
|
|
88
|
+
- Custom exception hierarchy: `V2RayFinderError`, `RateLimitError`, `AuthenticationError`, `NetworkError`, `TimeoutError`, `ParseError`, `RepositoryNotFoundError`
|
|
89
|
+
- `raise_errors=True` constructor flag for exception-based error handling
|
|
90
|
+
- `search_repos_or_empty()` and `get_repo_files_or_empty()` compatibility wrappers
|
|
91
|
+
|
|
92
|
+
- **Health Checking** (`health_checker` module)
|
|
93
|
+
- TCP connectivity verification with precise latency measurement
|
|
94
|
+
- Config format validation for vmess, vless, trojan, ss, ssr
|
|
95
|
+
- Concurrent batch health checks via asyncio semaphore
|
|
96
|
+
- Quality scoring (0–100) based on latency thresholds
|
|
97
|
+
- `filter_healthy_servers()` — filter by status and quality score
|
|
98
|
+
- `sort_by_quality()` — sort servers best-first
|
|
99
|
+
|
|
100
|
+
- **Secure Token Handling**
|
|
101
|
+
- Automatic `GITHUB_TOKEN` environment variable reading on init
|
|
102
|
+
- Token format validation and sanitization (length, character set, known prefixes)
|
|
103
|
+
- `V2RayServerFinder.from_env()` factory classmethod
|
|
104
|
+
- Security warnings logged when token passed directly as parameter
|
|
105
|
+
|
|
106
|
+
- **Rate Limit Tracking**
|
|
107
|
+
- `get_rate_limit_info()` — returns last known limit, remaining, and reset time
|
|
108
|
+
- Automatic warning logged when remaining requests drop below 10
|
|
109
|
+
|
|
110
|
+
- **Test Suite** (78% coverage)
|
|
111
|
+
- Unit tests for all core modules
|
|
112
|
+
- Async tests using `pytest-asyncio`
|
|
113
|
+
- Health checker tests with full TCP mocking
|
|
114
|
+
- CI matrix: Python 3.8–3.12 on Linux, macOS, and Windows
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
|
|
118
|
+
- `V2RayServerFinder.__init__` now accepts `raise_errors: bool = False`
|
|
119
|
+
- `search_repos()` now returns `Result[List[Dict], V2RayFinderError]` instead of raw list
|
|
120
|
+
- Rate limit checking moved after HTTP status checks to avoid mock-related issues
|
|
121
|
+
|
|
122
|
+
### Technical Notes
|
|
123
|
+
|
|
124
|
+
- No breaking changes for basic usage (`get_all_servers()`, `save_to_file()`)
|
|
125
|
+
- GUI module excluded from coverage (requires display server)
|
|
126
|
+
- All async code is Python 3.8-compatible (no `asyncio.run()` in public API)
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## [0.1.0] - 2026-01-15
|
|
131
|
+
|
|
132
|
+
### First Release
|
|
133
|
+
|
|
134
|
+
#### Added
|
|
135
|
+
|
|
136
|
+
**Core Functionality:**
|
|
137
|
+
- GitHub repository search for public V2Ray configs
|
|
138
|
+
- Curated direct subscription sources (3 reliable sources)
|
|
139
|
+
- Protocol support: vmess, vless, trojan, shadowsocks (ss), ssr
|
|
140
|
+
- Automatic deduplication of server configs
|
|
141
|
+
|
|
142
|
+
**Interfaces:**
|
|
143
|
+
- Python API (`V2RayServerFinder`)
|
|
144
|
+
- CLI (simple interactive TUI via `v2ray-finder`)
|
|
145
|
+
- Rich CLI with colored panels and progress bars (`v2ray-finder-rich`)
|
|
146
|
+
- GUI (PySide6/Qt via `v2ray-finder-gui`)
|
|
147
|
+
|
|
148
|
+
**Export:**
|
|
149
|
+
- Save server list to `.txt` files
|
|
150
|
+
- Protocol statistics display
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Project Statistics
|
|
155
|
+
|
|
156
|
+
| Metric | Value |
|
|
157
|
+
|--------|-------|
|
|
158
|
+
| Source Lines | ~2,500+ |
|
|
159
|
+
| Test Files | 8 |
|
|
160
|
+
| Test Coverage | ~82% |
|
|
161
|
+
| Supported Protocols | 5 (vmess, vless, trojan, ss, ssr) |
|
|
162
|
+
| Interfaces | 3 (Python API, CLI, GUI) |
|
|
163
|
+
| Python Versions | 3.8 – 3.12 |
|
|
164
|
+
| Platforms | Linux, macOS, Windows |
|
|
165
|
+
| Languages | 3 (\u0641\u0627\u0631\u0633\u06cc, English, Deutsch) |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Contributors
|
|
170
|
+
|
|
171
|
+
- Ali Sadeghi Aghili ([@alisadeghiaghili](https://github.com/alisadeghiaghili)) — Creator & Maintainer
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ali Sadeghi Aghili
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall
|
|
13
|
+
be included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
19
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
20
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: v2ray-finder
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Search and collect V2Ray servers from GitHub repositories
|
|
5
|
+
Author-email: Ali Sadeghi Aghili <alisadeghiaghili@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/alisadeghiaghili/v2ray-finder
|
|
8
|
+
Project-URL: Repository, https://github.com/alisadeghiaghili/v2ray-finder
|
|
9
|
+
Project-URL: Issues, https://github.com/alisadeghiaghili/v2ray-finder/issues
|
|
10
|
+
Project-URL: Source Code, https://github.com/alisadeghiaghili/v2ray-finder
|
|
11
|
+
Keywords: v2ray,proxy,vpn,github,vmess,vless,trojan,shadowsocks,subscription
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Topic :: Internet
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: requests>=2.31.0
|
|
28
|
+
Provides-Extra: gui
|
|
29
|
+
Requires-Dist: PySide6>=6.4.0; extra == "gui"
|
|
30
|
+
Provides-Extra: cli-rich
|
|
31
|
+
Requires-Dist: rich>=13.7.0; extra == "cli-rich"
|
|
32
|
+
Provides-Extra: async
|
|
33
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "async"
|
|
34
|
+
Requires-Dist: httpx>=0.24.0; extra == "async"
|
|
35
|
+
Provides-Extra: cache
|
|
36
|
+
Requires-Dist: diskcache>=5.6.0; extra == "cache"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: PySide6>=6.4.0; extra == "all"
|
|
39
|
+
Requires-Dist: rich>=13.7.0; extra == "all"
|
|
40
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "all"
|
|
41
|
+
Requires-Dist: httpx>=0.24.0; extra == "all"
|
|
42
|
+
Requires-Dist: diskcache>=5.6.0; extra == "all"
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
47
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
48
|
+
Requires-Dist: flake8>=6.0; extra == "dev"
|
|
49
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
50
|
+
Requires-Dist: isort>=5.12; extra == "dev"
|
|
51
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "dev"
|
|
52
|
+
Requires-Dist: httpx>=0.24.0; extra == "dev"
|
|
53
|
+
Requires-Dist: diskcache>=5.6.0; extra == "dev"
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
# v2ray-finder
|
|
57
|
+
|
|
58
|
+
[](https://badge.fury.io/py/v2ray-finder)
|
|
59
|
+
[](https://pypi.org/project/v2ray-finder/)
|
|
60
|
+
[](https://github.com/alisadeghiaghili/v2ray-finder/actions)
|
|
61
|
+
[](https://github.com/alisadeghiaghili/v2ray-finder/actions)
|
|
62
|
+
[](https://opensource.org/licenses/MIT)
|
|
63
|
+
[](https://github.com/psf/black)
|
|
64
|
+
[](https://github.com/alisadeghiaghili/v2ray-finder/stargazers)
|
|
65
|
+
|
|
66
|
+
[English](README.en.md) | [فارسی](README.fa.md) | [Deutsch](README.de.md) | [📋 CHANGELOG](CHANGELOG.md)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
A **high-performance** tool to **fetch, aggregate, validate and health-check public V2Ray server configs** from GitHub and curated subscription sources.
|
|
72
|
+
|
|
73
|
+
هدف این ابزار این است که بدون دردسر، یک لیست تمیز و dedup شده از لینکهای `vmess://`، `vless://`، `trojan://`، `ss://`، `ssr://` بهت بده.
|
|
74
|
+
|
|
75
|
+
**با عشق برای آزادی همیشگی ❤️**
|
|
76
|
+
**Built with love for eternal freedom ❤️**
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🚀 What's New in v0.2.0
|
|
81
|
+
|
|
82
|
+
### 🎉 Major Performance & Reliability Release!
|
|
83
|
+
|
|
84
|
+
⚡ **Async HTTP Fetching** — 10-50x faster concurrent downloads
|
|
85
|
+
💾 **Smart Caching** — 80-95% fewer GitHub API calls
|
|
86
|
+
🛡️ **Enhanced Error Handling** — Result type + custom exception hierarchy
|
|
87
|
+
🔒 **Secure Token Handling** — Environment variable support + `from_env()`
|
|
88
|
+
🧪 **78% Test Coverage** — Comprehensive test suite across Python 3.8–3.12
|
|
89
|
+
📈 **Rate Limit Tracking** — Monitor GitHub API usage
|
|
90
|
+
🏥 **Health Checking** — TCP connectivity, latency measurement, quality scoring
|
|
91
|
+
⌨️ **Interactive Token Prompt** — Secure masked input with `--prompt-token`
|
|
92
|
+
⛔ **Graceful Interruption** — Press Ctrl+C to save partial results
|
|
93
|
+
|
|
94
|
+
> See full details in [📋 CHANGELOG.md](CHANGELOG.md)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 🎯 Features / ویژگیها
|
|
99
|
+
|
|
100
|
+
### Core Features / ویژگیهای اصلی
|
|
101
|
+
- 🔍 **GitHub repository search** + **curated sources**
|
|
102
|
+
- 🚀 **Three interfaces**: Python API, CLI (simple & rich), GUI (PySide6)
|
|
103
|
+
- 📦 **Deduplicated** and **clean** output
|
|
104
|
+
- 🌐 **Supports**: vmess, vless, trojan, shadowsocks, ssr
|
|
105
|
+
- 💾 **Export** to text files
|
|
106
|
+
- 📊 **Statistics** by protocol
|
|
107
|
+
|
|
108
|
+
### Performance & Reliability / کارایی و قابلیت اطمینان
|
|
109
|
+
- ⚡ **Async HTTP fetching**: **10-50x faster** concurrent downloads
|
|
110
|
+
- 💾 **Smart caching**: **80-95% fewer** API calls with memory/disk cache
|
|
111
|
+
- ✅ **Health checking**: TCP connectivity, latency measurement, config validation
|
|
112
|
+
- 🎯 **Quality scoring**: Rank servers by speed and reliability
|
|
113
|
+
- 🔄 **Retry logic**: Automatic retry with exponential backoff
|
|
114
|
+
- ⛔ **Graceful interruption**: Ctrl+C saves partial results before exit
|
|
115
|
+
|
|
116
|
+
### Developer Experience / تجربه توسعهدهنده
|
|
117
|
+
- 🛡️ **Robust error handling**: Detailed exception hierarchy with proper error propagation
|
|
118
|
+
- 📈 **Rate limit tracking**: Monitor GitHub API usage
|
|
119
|
+
- 🔒 **Secure token handling**: Environment variable support with validation
|
|
120
|
+
- ⌨️ **Interactive token prompt**: Masked input for secure token entry
|
|
121
|
+
- 🧪 **78% test coverage**: Comprehensive test suite across Linux, macOS, and Windows
|
|
122
|
+
- ✅ **CI/CD**: Automated testing and deployment
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 📋 Requirements / پیشنیازها
|
|
127
|
+
|
|
128
|
+
- **Python** ≥ 3.8
|
|
129
|
+
- **Internet connection**
|
|
130
|
+
- **Optional**: aiohttp/httpx (async), diskcache (caching), PySide6 (GUI)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 📦 Installation / نصب
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Core + lightweight CLI
|
|
138
|
+
pip install v2ray-finder
|
|
139
|
+
|
|
140
|
+
# With async support (10-50x faster!)
|
|
141
|
+
pip install "v2ray-finder[async]"
|
|
142
|
+
|
|
143
|
+
# With caching (80-95% fewer API calls!)
|
|
144
|
+
pip install "v2ray-finder[cache]"
|
|
145
|
+
|
|
146
|
+
# With GUI support (PySide6)
|
|
147
|
+
pip install "v2ray-finder[gui]"
|
|
148
|
+
|
|
149
|
+
# With Rich CLI
|
|
150
|
+
pip install "v2ray-finder[cli-rich]"
|
|
151
|
+
|
|
152
|
+
# Everything (recommended)
|
|
153
|
+
pip install "v2ray-finder[all]"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### From source / از سورس
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone https://github.com/alisadeghiaghili/v2ray-finder.git
|
|
160
|
+
cd v2ray-finder
|
|
161
|
+
pip install -e ".[all,dev]"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🔒 Token Security / امنیت Token
|
|
167
|
+
|
|
168
|
+
### Method 1: Environment Variable (Recommended)
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# پیشنهادی / Recommended
|
|
172
|
+
export GITHUB_TOKEN="ghp_your_token_here"
|
|
173
|
+
v2ray-finder -s
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from v2ray_finder import V2RayServerFinder
|
|
178
|
+
|
|
179
|
+
finder = V2RayServerFinder() # reads GITHUB_TOKEN automatically
|
|
180
|
+
finder = V2RayServerFinder.from_env() # explicit
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Method 2: Interactive Prompt (New! ✨)
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Secure masked input
|
|
187
|
+
v2ray-finder --prompt-token -s -o servers.txt
|
|
188
|
+
v2ray-finder-rich --prompt-token
|
|
189
|
+
|
|
190
|
+
# In interactive mode (no args), you'll be prompted automatically
|
|
191
|
+
v2ray-finder-rich
|
|
192
|
+
# → "Do you want to provide a GitHub token? (y/n)"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Rate Limits:** without token: 60 req/h — with token: 5000 req/h
|
|
196
|
+
|
|
197
|
+
Generate a token at **GitHub Settings → Developer settings → Personal access tokens** with **public_repo** scope.
|
|
198
|
+
|
|
199
|
+
> ⚠️ **Security Note:** Never use `-t` flag for tokens (insecure). Use env var or `--prompt-token` instead.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## ⛔ Graceful Interruption (New! ✨)
|
|
204
|
+
|
|
205
|
+
**Press Ctrl+C at any time** during fetch operations to:
|
|
206
|
+
- Stop immediately without data loss
|
|
207
|
+
- Save all servers collected so far
|
|
208
|
+
- Display statistics for partial results
|
|
209
|
+
- Exit cleanly with code `130`
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
v2ray-finder -s -o servers.txt
|
|
213
|
+
# ... fetching ...
|
|
214
|
+
# Press Ctrl+C
|
|
215
|
+
|
|
216
|
+
[!] Interrupted by user. Saving partial results...
|
|
217
|
+
[✓] Saved 47 servers to v2ray_servers_partial.txt
|
|
218
|
+
|
|
219
|
+
Total servers: 47
|
|
220
|
+
By protocol:
|
|
221
|
+
vmess: 23
|
|
222
|
+
vless: 15
|
|
223
|
+
trojan: 9
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Rich CLI** version:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
v2ray-finder-rich -s
|
|
230
|
+
# Press Ctrl+C during fetch
|
|
231
|
+
|
|
232
|
+
⚠ Interrupted by user
|
|
233
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
|
|
234
|
+
✓ Saved 47 servers to v2ray_servers_partial.txt
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
> 📖 **See detailed guide:** [docs/INTERRUPTION_GUIDE.md](docs/INTERRUPTION_GUIDE.md)
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 📚 Library Usage / استفاده بهصورت کتابخانه
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from v2ray_finder import V2RayServerFinder
|
|
245
|
+
|
|
246
|
+
finder = V2RayServerFinder()
|
|
247
|
+
|
|
248
|
+
# Fast: curated sources only
|
|
249
|
+
servers = finder.get_all_servers()
|
|
250
|
+
print(f"Total: {len(servers)}")
|
|
251
|
+
|
|
252
|
+
# Extended: curated + GitHub search
|
|
253
|
+
servers = finder.get_all_servers(use_github_search=True)
|
|
254
|
+
|
|
255
|
+
# Save to file
|
|
256
|
+
count, filename = finder.save_to_file(filename="v2ray_servers.txt", limit=200)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Error Handling
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from v2ray_finder import V2RayServerFinder, RateLimitError, NetworkError
|
|
263
|
+
|
|
264
|
+
# Method 1: Result type
|
|
265
|
+
result = finder.search_repos(keywords=["v2ray"])
|
|
266
|
+
if result.is_ok():
|
|
267
|
+
repos = result.unwrap()
|
|
268
|
+
else:
|
|
269
|
+
print(result.error)
|
|
270
|
+
|
|
271
|
+
# Method 2: Exception mode
|
|
272
|
+
finder = V2RayServerFinder(raise_errors=True)
|
|
273
|
+
try:
|
|
274
|
+
repos = finder.search_repos_or_empty()
|
|
275
|
+
except RateLimitError as e:
|
|
276
|
+
print(f"Rate limit: {e}")
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Health Checking
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
servers = finder.get_servers_with_health(
|
|
283
|
+
check_health=True,
|
|
284
|
+
health_timeout=5.0,
|
|
285
|
+
min_quality_score=60.0,
|
|
286
|
+
filter_unhealthy=True,
|
|
287
|
+
)
|
|
288
|
+
for s in servers[:10]:
|
|
289
|
+
print(f"{s['protocol']:8s} | Quality: {s['quality_score']:5.1f} | {s['latency_ms']:6.1f}ms")
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## ⚡ CLI Usage / استفاده از CLI
|
|
295
|
+
|
|
296
|
+
### Basic CLI
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
export GITHUB_TOKEN="ghp_your_token_here"
|
|
300
|
+
|
|
301
|
+
v2ray-finder # Interactive TUI
|
|
302
|
+
v2ray-finder -o servers.txt # Quick save
|
|
303
|
+
v2ray-finder -s -l 200 -o servers.txt # GitHub search + limit
|
|
304
|
+
v2ray-finder --stats-only # Stats only
|
|
305
|
+
v2ray-finder --prompt-token -s # Secure token input
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**With health checking:**
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
v2ray-finder -c --min-quality 60 -o healthy_servers.txt
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Rich CLI (Recommended)
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
pip install "v2ray-finder[cli-rich]"
|
|
318
|
+
v2ray-finder-rich # Beautiful Rich TUI
|
|
319
|
+
v2ray-finder-rich --prompt-token # With secure token prompt
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Interactive mode features:**
|
|
323
|
+
- Token prompt on first run (if not in env)
|
|
324
|
+
- Press Ctrl+C during fetch → saves partial results
|
|
325
|
+
- Visual progress bars and spinners
|
|
326
|
+
- Color-coded health status
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## 🖥️ GUI / رابط گرافیکی
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
pip install "v2ray-finder[gui]"
|
|
334
|
+
v2ray-finder-gui
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 🛠️ Advanced Usage
|
|
340
|
+
|
|
341
|
+
### Interruption in Scripts
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
#!/bin/bash
|
|
345
|
+
|
|
346
|
+
v2ray-finder -s -o servers.txt
|
|
347
|
+
exit_code=$?
|
|
348
|
+
|
|
349
|
+
if [ $exit_code -eq 0 ]; then
|
|
350
|
+
echo "Success!"
|
|
351
|
+
# Process servers.txt
|
|
352
|
+
elif [ $exit_code -eq 130 ]; then
|
|
353
|
+
echo "Interrupted - using partial results"
|
|
354
|
+
mv v2ray_servers_partial.txt servers.txt
|
|
355
|
+
else
|
|
356
|
+
echo "Error occurred"
|
|
357
|
+
exit 1
|
|
358
|
+
fi
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### CI/CD with Timeout
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Timeout after 2 minutes, use partial results
|
|
365
|
+
timeout 120 v2ray-finder -s -o servers.txt || {
|
|
366
|
+
if [ $? -eq 124 ]; then
|
|
367
|
+
mv v2ray_servers_partial.txt servers.txt
|
|
368
|
+
fi
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 🤝 Contributing / مشارکت
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
pytest tests/ -v
|
|
378
|
+
black . && isort . && flake8 src/
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 📝 License
|
|
384
|
+
|
|
385
|
+
MIT License © 2026 Ali Sadeghi Aghili
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## 🔗 Links
|
|
390
|
+
|
|
391
|
+
- [Repository](https://github.com/alisadeghiaghili/v2ray-finder)
|
|
392
|
+
- [PyPI](https://pypi.org/project/v2ray-finder)
|
|
393
|
+
- [Issues](https://github.com/alisadeghiaghili/v2ray-finder/issues)
|
|
394
|
+
- [CHANGELOG](CHANGELOG.md)
|
|
395
|
+
- [Interruption Guide](docs/INTERRUPTION_GUIDE.md)
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 🙏 Acknowledgments / تشکرات
|
|
400
|
+
|
|
401
|
+
- [ebrasha/free-v2ray-public-list](https://github.com/ebrasha/free-v2ray-public-list)
|
|
402
|
+
- [barry-far/V2ray-Config](https://github.com/barry-far/V2ray-Config)
|
|
403
|
+
- [Epodonios/v2ray-configs](https://github.com/Epodonios/v2ray-configs)
|
|
404
|
+
|
|
405
|
+
و تمامی توسعهدهندگانی که کانفیگهای آزاد منتشر میکنند ❤️
|