proxy-scraper-cli 1.13.0__tar.gz → 1.15.0__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.
Files changed (117) hide show
  1. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/PKG-INFO +12 -4
  2. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/README.md +11 -3
  3. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/PKG-INFO +12 -4
  4. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/SOURCES.txt +1 -0
  5. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/__init__.py +1 -1
  6. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/cli.py +52 -0
  7. proxy_scraper_cli-1.15.0/tests/test_pick.py +44 -0
  8. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/LICENSE +0 -0
  9. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/dependency_links.txt +0 -0
  10. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/entry_points.txt +0 -0
  11. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/requires.txt +0 -0
  12. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxy_scraper_cli.egg-info/top_level.txt +0 -0
  13. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/__main__.py +0 -0
  14. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/action.py +0 -0
  15. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/agent.py +0 -0
  16. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/api.py +0 -0
  17. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/app.py +0 -0
  18. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/asndb.py +0 -0
  19. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/blocklist.py +0 -0
  20. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/charts.py +0 -0
  21. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/checker.py +0 -0
  22. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/compat.py +0 -0
  23. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/completion.py +0 -0
  24. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/exporters.py +0 -0
  25. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/fetchcache.py +0 -0
  26. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/geo.py +0 -0
  27. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/geodb.py +0 -0
  28. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/handshake.py +0 -0
  29. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/history.py +0 -0
  30. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/judges.py +0 -0
  31. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/mcp_entry.py +0 -0
  32. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/mcp_server.py +0 -0
  33. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/netio.py +0 -0
  34. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/options.py +0 -0
  35. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/output.py +0 -0
  36. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/pages.py +0 -0
  37. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/parsing.py +0 -0
  38. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/paths.py +0 -0
  39. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/pipeline.py +0 -0
  40. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/preferences.py +0 -0
  41. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/proxypages.py +0 -0
  42. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/publish.py +0 -0
  43. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/report.py +0 -0
  44. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/runsteps.py +0 -0
  45. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/__init__.py +0 -0
  46. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/core.py +0 -0
  47. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/http.py +0 -0
  48. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/pool.py +0 -0
  49. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/socks.py +0 -0
  50. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/status.py +0 -0
  51. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/server/upstream.py +0 -0
  52. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/site/apple-touch-icon.png +0 -0
  53. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/site/googleaac1161b7853c5b5.html +0 -0
  54. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/site/index.html +0 -0
  55. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/site/logo.png +0 -0
  56. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/site/og.png +0 -0
  57. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/sites.py +0 -0
  58. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/sources.json +0 -0
  59. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/sources.py +0 -0
  60. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/speed.py +0 -0
  61. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/targets.py +0 -0
  62. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/__init__.py +0 -0
  63. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/dashboard.py +0 -0
  64. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/keys.py +0 -0
  65. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/report.py +0 -0
  66. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/serve.py +0 -0
  67. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/widgets.py +0 -0
  68. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/proxyscraper/ui/wizard.py +0 -0
  69. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/pyproject.toml +0 -0
  70. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/setup.cfg +0 -0
  71. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_action.py +0 -0
  72. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_agent.py +0 -0
  73. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_api.py +0 -0
  74. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_app.py +0 -0
  75. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_asndb.py +0 -0
  76. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_auth.py +0 -0
  77. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_blocklist.py +0 -0
  78. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_charts.py +0 -0
  79. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_checker.py +0 -0
  80. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_compat.py +0 -0
  81. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_completion.py +0 -0
  82. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_discovery_growth.py +0 -0
  83. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_exporters.py +0 -0
  84. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_fetchcache.py +0 -0
  85. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_geodb.py +0 -0
  86. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_judges.py +0 -0
  87. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_learning.py +0 -0
  88. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_live_api.py +0 -0
  89. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_mcp_server.py +0 -0
  90. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_mine_sources.py +0 -0
  91. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_options.py +0 -0
  92. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_output_stdout.py +0 -0
  93. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_packaging.py +0 -0
  94. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_pages.py +0 -0
  95. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_parsing.py +0 -0
  96. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_parsing_fuzz.py +0 -0
  97. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_paths.py +0 -0
  98. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_pipeline.py +0 -0
  99. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_proxy_pages.py +0 -0
  100. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_proxy_sources.py +0 -0
  101. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_publish.py +0 -0
  102. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_report.py +0 -0
  103. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_robustness.py +0 -0
  104. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_serve_password.py +0 -0
  105. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_serve_refill.py +0 -0
  106. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_serve_v2.py +0 -0
  107. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_server.py +0 -0
  108. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_server_closed_writer.py +0 -0
  109. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_server_fixes.py +0 -0
  110. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_sites.py +0 -0
  111. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_sources_json.py +0 -0
  112. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_speed.py +0 -0
  113. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_tamper.py +0 -0
  114. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_targets.py +0 -0
  115. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_ui.py +0 -0
  116. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_uptime.py +0 -0
  117. {proxy_scraper_cli-1.13.0 → proxy_scraper_cli-1.15.0}/tests/test_wizard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy-scraper-cli
3
- Version: 1.13.0
3
+ Version: 1.15.0
4
4
  Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
5
5
  Author: Maximilian Feix
6
6
  License-Expression: MIT
@@ -88,7 +88,7 @@ Most free proxy lists are 95 % dead, and a good part of the rest are honeypots o
88
88
  - [Examples](#examples)
89
89
  - [Recipes](#recipes)
90
90
  - [Rotating proxy server](#proxy-server)
91
- - [Discord bot](#discord-bot)
91
+ - [Discord and Telegram bot](#discord-bot)
92
92
  - [How it works](#how-it-works)
93
93
  - [Output](#output)
94
94
  - [Options](#options)
@@ -494,9 +494,9 @@ In testing: 20 of 20 HTTPS requests succeeded, over 15 different exit IPs. In th
494
494
 
495
495
  <a id="discord-bot"></a>
496
496
 
497
- ## Discord bot
497
+ ## Discord and Telegram bot
498
498
 
499
- The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. Setup in [bot/README.md](bot/README.md).
499
+ The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. The same process answers on Telegram too: `/proxy de socks5` for one proxy with a curl line, `/proxies 10 us https` for a short list. Setup in [bot/README.md](bot/README.md).
500
500
 
501
501
  <a id="how-it-works"></a>
502
502
 
@@ -699,6 +699,13 @@ pip download requests # git, pip, npm & co. now go through the pool
699
699
 
700
700
  `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
701
701
 
702
+ **In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
703
+
704
+ ```bash
705
+ curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
706
+ proxy-scraper --pick 10 --country DE --works-on google > de.txt
707
+ ```
708
+
702
709
  **Without installing anything** – straight from the live list
703
710
 
704
711
  ```bash
@@ -743,6 +750,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
743
750
  | `--discover` | search GitHub for new sources right now |
744
751
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
745
752
  | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
753
+ | `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
746
754
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
747
755
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
748
756
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
@@ -47,7 +47,7 @@ Most free proxy lists are 95 % dead, and a good part of the rest are honeypots o
47
47
  - [Examples](#examples)
48
48
  - [Recipes](#recipes)
49
49
  - [Rotating proxy server](#proxy-server)
50
- - [Discord bot](#discord-bot)
50
+ - [Discord and Telegram bot](#discord-bot)
51
51
  - [How it works](#how-it-works)
52
52
  - [Output](#output)
53
53
  - [Options](#options)
@@ -453,9 +453,9 @@ In testing: 20 of 20 HTTPS requests succeeded, over 15 different exit IPs. In th
453
453
 
454
454
  <a id="discord-bot"></a>
455
455
 
456
- ## Discord bot
456
+ ## Discord and Telegram bot
457
457
 
458
- The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. Setup in [bot/README.md](bot/README.md).
458
+ The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. The same process answers on Telegram too: `/proxy de socks5` for one proxy with a curl line, `/proxies 10 us https` for a short list. Setup in [bot/README.md](bot/README.md).
459
459
 
460
460
  <a id="how-it-works"></a>
461
461
 
@@ -658,6 +658,13 @@ pip download requests # git, pip, npm & co. now go through the pool
658
658
 
659
659
  `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
660
660
 
661
+ **In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
662
+
663
+ ```bash
664
+ curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
665
+ proxy-scraper --pick 10 --country DE --works-on google > de.txt
666
+ ```
667
+
661
668
  **Without installing anything** – straight from the live list
662
669
 
663
670
  ```bash
@@ -702,6 +709,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
702
709
  | `--discover` | search GitHub for new sources right now |
703
710
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
704
711
  | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
712
+ | `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
705
713
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
706
714
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
707
715
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy-scraper-cli
3
- Version: 1.13.0
3
+ Version: 1.15.0
4
4
  Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
5
5
  Author: Maximilian Feix
6
6
  License-Expression: MIT
@@ -88,7 +88,7 @@ Most free proxy lists are 95 % dead, and a good part of the rest are honeypots o
88
88
  - [Examples](#examples)
89
89
  - [Recipes](#recipes)
90
90
  - [Rotating proxy server](#proxy-server)
91
- - [Discord bot](#discord-bot)
91
+ - [Discord and Telegram bot](#discord-bot)
92
92
  - [How it works](#how-it-works)
93
93
  - [Output](#output)
94
94
  - [Options](#options)
@@ -494,9 +494,9 @@ In testing: 20 of 20 HTTPS requests succeeded, over 15 different exit IPs. In th
494
494
 
495
495
  <a id="discord-bot"></a>
496
496
 
497
- ## Discord bot
497
+ ## Discord and Telegram bot
498
498
 
499
- The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. Setup in [bot/README.md](bot/README.md).
499
+ The live list can also come to you: [`bot/`](bot/) is a Discord bot that posts every run into a server – a summary with the fastest proxies, the full lists per protocol as files, and slash commands like `/proxies type:socks5 country:DE https:true`. It sets up its own read-only channels when you invite it, and a GitHub Action deploys it to a server as a systemd service. The same process answers on Telegram too: `/proxy de socks5` for one proxy with a curl line, `/proxies 10 us https` for a short list. Setup in [bot/README.md](bot/README.md).
500
500
 
501
501
  <a id="how-it-works"></a>
502
502
 
@@ -699,6 +699,13 @@ pip download requests # git, pip, npm & co. now go through the pool
699
699
 
700
700
  `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
701
701
 
702
+ **In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
703
+
704
+ ```bash
705
+ curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
706
+ proxy-scraper --pick 10 --country DE --works-on google > de.txt
707
+ ```
708
+
702
709
  **Without installing anything** – straight from the live list
703
710
 
704
711
  ```bash
@@ -743,6 +750,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
743
750
  | `--discover` | search GitHub for new sources right now |
744
751
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
745
752
  | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
753
+ | `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
746
754
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
747
755
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
748
756
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
@@ -92,6 +92,7 @@ tests/test_pages.py
92
92
  tests/test_parsing.py
93
93
  tests/test_parsing_fuzz.py
94
94
  tests/test_paths.py
95
+ tests/test_pick.py
95
96
  tests/test_pipeline.py
96
97
  tests/test_proxy_pages.py
97
98
  tests/test_proxy_sources.py
@@ -1,7 +1,7 @@
1
1
  """proxy-scraper: collects public HTTP/SOCKS4/SOCKS5 proxies from hundreds of sources, checks them
2
2
  in parallel with its own protocol handshakes and learns which sources deliver good proxies."""
3
3
 
4
- __version__ = "1.13.0"
4
+ __version__ = "1.15.0"
5
5
 
6
6
 
7
7
  def __getattr__(name):
@@ -34,6 +34,7 @@ from .output import has_latest_results
34
34
  from .parsing import PROXY_TYPES
35
35
  from .preferences import load_last_argv, save_last_argv
36
36
  from .server.pool import STRATEGIES
37
+ from .sites import SITES
37
38
  from .targets import parse_target
38
39
  from .ui import ACCENT, BAD, MUTED, banner, note, render_source_ranking, widgets
39
40
  from .ui.keys import is_interactive
@@ -62,6 +63,36 @@ def list_sources(limit: int, as_json: bool = False) -> int:
62
63
  return 0
63
64
 
64
65
 
66
+ def pick(args) -> int:
67
+ """--pick: proxies from the hourly list, nothing but their URLs on stdout (for $(…) in a shell)."""
68
+ from .api import live_proxies
69
+
70
+ try:
71
+ found = live_proxies(types=args.types, countries=args.country or "", https=args.https_only,
72
+ anonymity=args.anonymity or "", max_latency=args.max_latency,
73
+ no_datacenter=args.no_datacenter, no_blocklisted=args.no_blocklisted,
74
+ min_uptime=args.min_uptime, works_on=args.works_on, limit=args.pick)
75
+ except (ConnectionError, ValueError) as e:
76
+ print(f"proxy-scraper: {e}", file=sys.stderr)
77
+ return 1
78
+ if not found:
79
+ print("proxy-scraper: no proxy in the live list matches these filters right now – loosen them, "
80
+ "or run a scan of your own", file=sys.stderr)
81
+ return 1
82
+ sys.stdout.write("".join(f"{p.url}\n" for p in found))
83
+ return 0
84
+
85
+
86
+ def site_list(text: str) -> List[str]:
87
+ """argparse type for --works-on: google,reddit -> ["google", "reddit"]"""
88
+ names = [n.strip().lower() for n in text.split(",") if n.strip()]
89
+ known = [s.name for s in SITES]
90
+ for n in names:
91
+ if n not in known:
92
+ raise argparse.ArgumentTypeError(f"unknown site {n!r}, use {', '.join(known)}")
93
+ return names
94
+
95
+
65
96
  def _number(kind, minimum, strict=False, maximum=None):
66
97
  """argparse type for numbers with limits – with an understandable error message."""
67
98
  def parse(text: str):
@@ -96,6 +127,7 @@ def export_list(text: str) -> List[str]:
96
127
  positive_int = _number(int, 1)
97
128
  port_number = _number(int, 1, maximum=65535)
98
129
  non_negative_int = _number(int, 0)
130
+ percent = _number(int, 0, maximum=100)
99
131
  positive_float = _number(float, 0, strict=True)
100
132
  non_negative_float = _number(float, 0)
101
133
 
@@ -166,6 +198,16 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
166
198
  f.add_argument("--target", action="append", type=target_url, metavar="URL",
167
199
  help="only proxies that reach this site (repeatable), e.g. --target google.com")
168
200
 
201
+ live = p.add_argument_group("Live list (no scan)")
202
+ live.add_argument("--pick", nargs="?", const=1, type=positive_int, metavar="N",
203
+ help="print N proxies (default 1) from the hourly checked list that match the filters above, "
204
+ "one URL per line, and exit – e.g. curl -x \"$(proxy-scraper --pick --https-only)\" …")
205
+ live.add_argument("--min-uptime", type=percent, default=0, metavar="PERCENT",
206
+ help="with --pick: only proxies on the list in at least this share of the week's checks")
207
+ live.add_argument("--works-on", type=site_list, default=[], metavar="SITES",
208
+ help=f"with --pick: only proxies that got through to these sites in the last check "
209
+ f"({', '.join(s.name for s in SITES)})")
210
+
169
211
  v = p.add_argument_group("Proxy server")
170
212
  v.add_argument("--serve", nargs="?", const=DEFAULT_SERVE_PORT, default=0, type=port_number, metavar="PORT",
171
213
  help=f"serve the hits as a rotating proxy on 127.0.0.1:PORT after the run "
@@ -209,6 +251,14 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
209
251
  args = p.parse_args(argv)
210
252
  if args.json and args.list_sources is None:
211
253
  p.error("--json only works together with --list-sources")
254
+ if (args.min_uptime or args.works_on) and args.pick is None:
255
+ p.error("--min-uptime and --works-on only work together with --pick")
256
+ if args.pick is not None: # --pick exits right away: options for a scan or the server would be silently ignored
257
+ scan_only = [flag for dest, flag in (("target", "--target"), ("serve", "--serve"), ("recheck", "--recheck"),
258
+ ("want", "--want"), ("output", "-o/--output"), ("export", "--export"))
259
+ if getattr(args, dest) != p.get_default(dest)]
260
+ if scan_only:
261
+ p.error(f"--pick takes proxies from the live list and exits – {', '.join(scan_only)} belong to a scan")
212
262
  return args
213
263
 
214
264
 
@@ -260,6 +310,8 @@ def run(argv: Optional[List[str]] = None) -> int:
260
310
  return mcp_main()
261
311
  if args.list_sources is not None:
262
312
  return list_sources(args.list_sources, args.json)
313
+ if args.pick is not None:
314
+ return pick(args)
263
315
  opts = RunOptions.from_args(args)
264
316
  if opts.output == STDOUT:
265
317
  widgets.console = Console(highlight=False, stderr=True) # stdout only carries the hits
@@ -0,0 +1,44 @@
1
+ """proxy-scraper --pick: proxies from the hourly list on stdout, with the usual filters, no scan."""
2
+
3
+ import pytest
4
+
5
+ from proxyscraper import api, cli
6
+ from tests.test_live_api import ROWS, STATS, fetch_from
7
+
8
+
9
+ @pytest.fixture(autouse=True)
10
+ def offline(monkeypatch):
11
+ rows = [dict(ROWS[0], sites={"google": True}), ROWS[1], ROWS[2]]
12
+ monkeypatch.setattr(api, "_live_fetch", fetch_from({"proxies.json": rows, "stats.json": STATS}))
13
+
14
+
15
+ def test_one_proxy_by_default_and_nothing_else_on_stdout(capsys):
16
+ assert cli.run(["--pick"]) == 0
17
+ assert capsys.readouterr().out == "http://2.2.2.2:80\n" # the fastest
18
+
19
+
20
+ def test_the_usual_filters_apply(capsys):
21
+ assert cli.run(["--pick", "5", "--types", "socks4", "socks5", "--country", "DE", "--https-only"]) == 0
22
+ assert capsys.readouterr().out.splitlines() == ["socks4://3.3.3.3:4145", "socks5://1.1.1.1:1080"]
23
+ assert cli.run(["--pick", "--min-uptime", "90", "--works-on", "google"]) == 0
24
+ assert capsys.readouterr().out == "socks5://1.1.1.1:1080\n"
25
+
26
+
27
+ def test_nothing_matching_is_exit_1_with_a_hint_on_stderr(capsys):
28
+ assert cli.run(["--pick", "--country", "JP"]) == 1
29
+ out = capsys.readouterr()
30
+ assert out.out == "" and "no proxy" in out.err.lower()
31
+
32
+
33
+ def test_pick_only_filters_need_pick(capsys):
34
+ with pytest.raises(SystemExit):
35
+ cli.run(["--min-uptime", "90"])
36
+ assert "--pick" in capsys.readouterr().err
37
+
38
+
39
+ @pytest.mark.parametrize("extra", [["--target", "discord.com"], ["--serve"], ["--recheck"], ["--want", "5"],
40
+ ["-o", "x.txt"], ["--export", "clash"]])
41
+ def test_options_for_a_scan_are_an_error_not_ignored(extra, capsys):
42
+ with pytest.raises(SystemExit):
43
+ cli.run(["--pick", *extra])
44
+ assert "--pick" in capsys.readouterr().err