diff --git a/.github/workflows/update_hosts.yml b/.github/workflows/update_hosts.yml index c098ac6..7b7fce8 100644 --- a/.github/workflows/update_hosts.yml +++ b/.github/workflows/update_hosts.yml @@ -7,7 +7,7 @@ on: - 'setHosts_Classic.py' - 'requirements.txt' branches: - - 'main' + - 'master' schedule: - cron: '0 */4 * * *' workflow_dispatch: @@ -16,6 +16,38 @@ permissions: contents: write jobs: + format-fix: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install formatting tools + run: | + pip install black flake8 isort + + - name: Run and fix formatting + run: | + black . + isort . + flake8 --ignore=E501,W503,E203 . + + # 提交修复的代码 + - name: Commit and push fixes + if: success() || failure() + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git add . + git commit -m "Fix formatting issues" || echo "No changes to commit" + git push + update-hosts: runs-on: ubuntu-latest @@ -37,12 +69,12 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip + python -m pip install pip sudo pip install -r requirements.txt - + - name: Run hosts update script1 - run: | - sudo python setHosts.py + run: | + sudo python3 setHosts_Classic.py # 提交并推送更新(仅在文件变更时) - name: Commit and push changes diff --git a/README.md b/README.md index 6163c8c..fb99368 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ > 适合部分地区饱受dns污染困扰,访问 GitHub 卡顿、抽风、图裂,无法使用Chrome浏览器 自带翻译功能,无法刮削影视封面等问题。分别使用 `setDNS` 自动查找最快服务器并设置,使用 `setHosts` 自动查找DNS映射主机并设置。支持Windows、Linux、MacOS。Enjoy!❤ > [!NOTE] -> 首次运行大约需要2分钟以获取DNS主机,请耐心等待。后续运行速度大约10秒左右 +> 首次运行大约需要2分钟以获取DNS主机并建立缓存,请耐心等待。后续运行速度大概二三十秒。 ## 一、使用方法 @@ -22,6 +22,10 @@ [![Release Detail](https://img.shields.io/github/v/release/sinspired/cnNetTool?sort=date&display_name=release&logo=github&label=Release)](https://github.com/sinspired/cnNetTool/releases/latest) +程序使用DNS服务器实时解析和DNS A、AAAA记录获取IPv4及IPv6地址,通过本地网络环境检测延迟并进行SSL证书验证。 + +由于需要进行 `hosts` 修改备份操作,exe文件已标记需要管理员权限,如果被系统误报病毒,请允许后再次操作。 + > 强烈建议采用本方法,如果喜欢折腾,可以继续往下看。 ### 1.2 手动操作 @@ -30,80 +34,130 @@ ```bash -# cnNetTool Start in 2024-12-05 16:28:37 +08:00 -140.82.114.26 alive.github.com -140.82.112.6 api.github.com -140.82.112.22 central.github.com -140.82.112.10 codeload.github.com -140.82.114.22 collector.github.com -140.82.113.4 gist.github.com -140.82.114.4 github.com -140.82.113.18 github.community -146.75.29.194 github.global.ssl.fastly.net -3.5.11.158 github-com.s3.amazonaws.com -52.216.236.243 github-production-release-asset-2e65be.s3.amazonaws.com -140.82.112.26 live.github.com +# cnNetTool Start in 2024-12-09 14:15:15 +08:00 +140.82.112.25 alive.github.com +140.82.113.25 live.github.com +20.205.243.168 api.github.com +20.205.243.165 codeload.github.com +140.82.114.22 central.github.com +140.82.112.3 gist.github.com +20.205.243.166 github.com +140.82.112.17 github.community +151.101.129.194 github.global.ssl.fastly.net +2a03:2880:f136:83:face:b00c:0:25de github.global.ssl.fastly.net +3.5.25.108 github-com.s3.amazonaws.com +3.5.28.157 github-production-release-asset-2e65be.s3.amazonaws.com +3.5.30.172 github-production-user-asset-6210df.s3.amazonaws.com +3.5.24.52 github-production-repository-file-5c1aeb.s3.amazonaws.com 13.107.42.16 pipelines.actions.githubusercontent.com -185.199.109.154 github.githubassets.com -185.199.108.153 github.io -185.199.108.153 githubstatus.com -185.199.108.153 assets-cdn.github.com -185.199.108.133 avatars.githubusercontent.com -185.199.108.133 avatars0.githubusercontent.com -185.199.108.133 avatars1.githubusercontent.com -185.199.108.133 avatars2.githubusercontent.com -185.199.108.133 avatars3.githubusercontent.com -185.199.108.133 avatars4.githubusercontent.com -185.199.108.133 avatars5.githubusercontent.com -185.199.108.133 camo.githubusercontent.com -185.199.108.133 cloud.githubusercontent.com -185.199.108.133 desktop.githubusercontent.com -185.199.108.133 favicons.githubusercontent.com -185.199.108.133 github.map.fastly.net -185.199.108.133 media.githubusercontent.com -185.199.108.133 objects.githubusercontent.com -185.199.108.133 private-user-images.githubusercontent.com -185.199.108.133 raw.githubusercontent.com -185.199.108.133 user-images.githubusercontent.com -18.160.200.103 tmdb.org -18.160.200.103 api.tmdb.org -18.160.200.103 files.tmdb.org -108.159.227.111 themoviedb.org -108.159.227.111 api.themoviedb.org -108.159.227.111 www.themoviedb.org -108.159.227.111 auth.themoviedb.org -169.150.236.97 image.tmdb.org -169.150.236.97 images.tmdb.org -52.94.237.74 imdb.com -3.168.35.144 www.imdb.com -52.94.237.74 secure.imdb.com -3.168.35.144 s.media-imdb.com -52.94.228.167 us.dd.imdb.com -3.168.35.144 www.imdb.to -52.94.237.74 imdb-webservice.amazon.com -44.215.137.99 origin-www.imdb.com -18.172.138.122 m.media-amazon.com -146.75.29.16 Images-na.ssl-images-amazon.com -18.160.216.100 images-fe.ssl-images-amazon.com -23.220.246.166 images-eu.ssl-images-amazon.com -146.75.29.16 ia.media-imdb.com -146.75.29.16 f.media-amazon.com -52.84.18.105 imdb-video.media-imdb.com -3.167.180.176 dqpnq362acqdi.cloudfront.net -142.250.191.170 translate.google.com -142.250.191.170 translate.googleapis.com -142.250.191.170 translate-pa.googleapis.com -54.230.18.115 plugins.jetbrains.com -54.230.18.115 download.jetbrains.com -54.230.18.115 cache-redirector.jetbrains.com - -# Update time: 2024-12-05 16:28:37 +08:00 +2620:1ec:22::16 pipelines.actions.githubusercontent.com +185.199.108.154 github.githubassets.com +16.182.106.1 github-cloud.s3.amazonaws.com +192.0.66.2 github.blog +185.199.109.153 githubstatus.com +2606:50c0:8003::153 githubstatus.com +185.199.109.153 assets-cdn.github.com +2606:50c0:8003::153 assets-cdn.github.com +185.199.109.153 github.io +2606:50c0:8003::153 github.io +140.82.114.22 collector.github.com +140.82.114.22 education.github.com +185.199.109.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com +185.199.109.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com +185.199.109.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com +185.199.109.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com +185.199.109.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com +185.199.109.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com +185.199.109.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com +185.199.109.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com +185.199.109.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com +185.199.109.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com +185.199.109.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com +185.199.109.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net +185.199.109.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com +185.199.109.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com +185.199.109.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com +185.199.109.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com +185.199.109.133 user-images.githubusercontent.com +2606:50c0:8002::154 user-images.githubusercontent.com +18.67.51.128 tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 tmdb.org +18.67.51.128 api.tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 api.tmdb.org +18.67.51.128 files.tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 files.tmdb.org +18.67.51.128 themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 themoviedb.org +18.67.51.128 api.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 api.themoviedb.org +18.67.51.128 www.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 www.themoviedb.org +18.67.51.128 auth.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 auth.themoviedb.org +143.244.50.84 image.tmdb.org +2400:52e0:1500::1092:1 image.tmdb.org +143.244.50.84 images.tmdb.org +2400:52e0:1500::1092:1 images.tmdb.org +13.35.217.192 imdb.com +13.35.217.192 www.imdb.com +13.35.217.192 secure.imdb.com +13.35.217.192 s.media-imdb.com +13.35.217.192 us.dd.imdb.com +13.35.217.192 www.imdb.to +13.35.217.192 imdb-webservice.amazon.com +13.35.217.192 origin-www.imdb.com +23.202.34.219 m.media-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 m.media-amazon.com +23.202.34.219 Images-na.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 Images-na.ssl-images-amazon.com +23.202.34.219 images-fe.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 images-fe.ssl-images-amazon.com +23.202.34.219 images-eu.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 images-eu.ssl-images-amazon.com +23.202.34.219 ia.media-imdb.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 ia.media-imdb.com +23.202.34.219 f.media-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 f.media-amazon.com +23.202.34.219 imdb-video.media-imdb.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 imdb-video.media-imdb.com +23.202.34.219 dqpnq362acqdi.cloudfront.net +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 dqpnq362acqdi.cloudfront.net +2404:6800:4005:81b::200a translate.google.com +2404:6800:4005:81b::200a translate.googleapis.com +2404:6800:4005:81b::200a translate-pa.googleapis.com +13.33.88.22 plugins.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 plugins.jetbrains.com +13.33.88.22 download.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 download.jetbrains.com +13.33.88.22 cache-redirector.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 cache-redirector.jetbrains.com + +# Update time: 2024-12-09 14:15:15 +08:00 # GitHub仓库: https://github.com/sinspired/cnNetTool # cnNetTool End ``` -以上内容会自动定时更新, 数据更新时间:2024-12-05 16:28:37 +08:00 +以上内容会自动定时更新, 数据更新时间:2024-12-09 14:15:15 +08:00 + +> [!NOTE] +> 由于数据获取于非本地网络环境,请自行测试可用性,否则请采用方法 1,使用本地网络环境自动设置。 #### 1.2.2 修改 hosts 文件 @@ -121,7 +175,7 @@ hosts 文件在每个系统的位置不一,详情如下: 3. iPhone、iPad 须越狱、Android 必须要 root。 > [!NOTE] -> 请先把 `hosts` 文件复制到其他目录,修改后再复制回去,否则可能无法修改。 +> Windows系统可能需要先把 `hosts` 文件复制到其他目录,修改后再复制回去,否则可能没有修改权限。 ## 二、安装 @@ -170,7 +224,7 @@ py SetHosts.py ``` 可执行文件也可带参数运行 ```pwsh -./SetDNS.exe --best-dns-num 10 +./SetDNS.exe --best-dns-num 10 --mode 'overall' --show-resolutions ./SetHosts.exe --num-fastest 3 --max-latency 500 ``` diff --git a/hosts b/hosts index 9a0009a..4dddc7f 100644 --- a/hosts +++ b/hosts @@ -1,71 +1,118 @@ -# cnNetTool Start in 2024-12-05 16:28:37 +08:00 -140.82.114.26 alive.github.com -140.82.112.6 api.github.com -140.82.112.22 central.github.com -140.82.112.10 codeload.github.com -140.82.114.22 collector.github.com -140.82.113.4 gist.github.com -140.82.114.4 github.com -140.82.113.18 github.community -146.75.29.194 github.global.ssl.fastly.net -3.5.11.158 github-com.s3.amazonaws.com -52.216.236.243 github-production-release-asset-2e65be.s3.amazonaws.com -140.82.112.26 live.github.com +# cnNetTool Start in 2024-12-09 14:15:15 +08:00 +140.82.112.25 alive.github.com +140.82.113.25 live.github.com +20.205.243.168 api.github.com +20.205.243.165 codeload.github.com +140.82.114.22 central.github.com +140.82.112.3 gist.github.com +20.205.243.166 github.com +140.82.112.17 github.community +151.101.129.194 github.global.ssl.fastly.net +2a03:2880:f136:83:face:b00c:0:25de github.global.ssl.fastly.net +3.5.25.108 github-com.s3.amazonaws.com +3.5.28.157 github-production-release-asset-2e65be.s3.amazonaws.com +3.5.30.172 github-production-user-asset-6210df.s3.amazonaws.com +3.5.24.52 github-production-repository-file-5c1aeb.s3.amazonaws.com 13.107.42.16 pipelines.actions.githubusercontent.com -185.199.109.154 github.githubassets.com -185.199.108.153 github.io -185.199.108.153 githubstatus.com -185.199.108.153 assets-cdn.github.com -185.199.108.133 avatars.githubusercontent.com -185.199.108.133 avatars0.githubusercontent.com -185.199.108.133 avatars1.githubusercontent.com -185.199.108.133 avatars2.githubusercontent.com -185.199.108.133 avatars3.githubusercontent.com -185.199.108.133 avatars4.githubusercontent.com -185.199.108.133 avatars5.githubusercontent.com -185.199.108.133 camo.githubusercontent.com -185.199.108.133 cloud.githubusercontent.com -185.199.108.133 desktop.githubusercontent.com -185.199.108.133 favicons.githubusercontent.com -185.199.108.133 github.map.fastly.net -185.199.108.133 media.githubusercontent.com -185.199.108.133 objects.githubusercontent.com -185.199.108.133 private-user-images.githubusercontent.com -185.199.108.133 raw.githubusercontent.com -185.199.108.133 user-images.githubusercontent.com -18.160.200.103 tmdb.org -18.160.200.103 api.tmdb.org -18.160.200.103 files.tmdb.org -108.159.227.111 themoviedb.org -108.159.227.111 api.themoviedb.org -108.159.227.111 www.themoviedb.org -108.159.227.111 auth.themoviedb.org -169.150.236.97 image.tmdb.org -169.150.236.97 images.tmdb.org -52.94.237.74 imdb.com -3.168.35.144 www.imdb.com -52.94.237.74 secure.imdb.com -3.168.35.144 s.media-imdb.com -52.94.228.167 us.dd.imdb.com -3.168.35.144 www.imdb.to -52.94.237.74 imdb-webservice.amazon.com -44.215.137.99 origin-www.imdb.com -18.172.138.122 m.media-amazon.com -146.75.29.16 Images-na.ssl-images-amazon.com -18.160.216.100 images-fe.ssl-images-amazon.com -23.220.246.166 images-eu.ssl-images-amazon.com -146.75.29.16 ia.media-imdb.com -146.75.29.16 f.media-amazon.com -52.84.18.105 imdb-video.media-imdb.com -3.167.180.176 dqpnq362acqdi.cloudfront.net -142.250.191.170 translate.google.com -142.250.191.170 translate.googleapis.com -142.250.191.170 translate-pa.googleapis.com -54.230.18.115 plugins.jetbrains.com -54.230.18.115 download.jetbrains.com -54.230.18.115 cache-redirector.jetbrains.com +2620:1ec:22::16 pipelines.actions.githubusercontent.com +185.199.108.154 github.githubassets.com +16.182.106.1 github-cloud.s3.amazonaws.com +192.0.66.2 github.blog +185.199.109.153 githubstatus.com +2606:50c0:8003::153 githubstatus.com +185.199.109.153 assets-cdn.github.com +2606:50c0:8003::153 assets-cdn.github.com +185.199.109.153 github.io +2606:50c0:8003::153 github.io +140.82.114.22 collector.github.com +140.82.114.22 education.github.com +185.199.109.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com +185.199.109.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com +185.199.109.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com +185.199.109.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com +185.199.109.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com +185.199.109.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com +185.199.109.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com +185.199.109.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com +185.199.109.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com +185.199.109.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com +185.199.109.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com +185.199.109.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net +185.199.109.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com +185.199.109.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com +185.199.109.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com +185.199.109.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com +185.199.109.133 user-images.githubusercontent.com +2606:50c0:8002::154 user-images.githubusercontent.com +18.67.51.128 tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 tmdb.org +18.67.51.128 api.tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 api.tmdb.org +18.67.51.128 files.tmdb.org +2600:9000:2816:d800:e:5373:440:93a1 files.tmdb.org +18.67.51.128 themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 themoviedb.org +18.67.51.128 api.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 api.themoviedb.org +18.67.51.128 www.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 www.themoviedb.org +18.67.51.128 auth.themoviedb.org +2600:9000:2816:d800:e:5373:440:93a1 auth.themoviedb.org +143.244.50.84 image.tmdb.org +2400:52e0:1500::1092:1 image.tmdb.org +143.244.50.84 images.tmdb.org +2400:52e0:1500::1092:1 images.tmdb.org +13.35.217.192 imdb.com +13.35.217.192 www.imdb.com +13.35.217.192 secure.imdb.com +13.35.217.192 s.media-imdb.com +13.35.217.192 us.dd.imdb.com +13.35.217.192 www.imdb.to +13.35.217.192 imdb-webservice.amazon.com +13.35.217.192 origin-www.imdb.com +23.202.34.219 m.media-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 m.media-amazon.com +23.202.34.219 Images-na.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 Images-na.ssl-images-amazon.com +23.202.34.219 images-fe.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 images-fe.ssl-images-amazon.com +23.202.34.219 images-eu.ssl-images-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 images-eu.ssl-images-amazon.com +23.202.34.219 ia.media-imdb.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 ia.media-imdb.com +23.202.34.219 f.media-amazon.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 f.media-amazon.com +23.202.34.219 imdb-video.media-imdb.com +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 imdb-video.media-imdb.com +23.202.34.219 dqpnq362acqdi.cloudfront.net +2600:9000:2079:4000:1d:d7f6:39d4:e6e1 dqpnq362acqdi.cloudfront.net +2404:6800:4005:81b::200a translate.google.com +2404:6800:4005:81b::200a translate.googleapis.com +2404:6800:4005:81b::200a translate-pa.googleapis.com +13.33.88.22 plugins.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 plugins.jetbrains.com +13.33.88.22 download.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 download.jetbrains.com +13.33.88.22 cache-redirector.jetbrains.com +2600:9000:2014:c00:12:7c44:15c0:93a1 cache-redirector.jetbrains.com -# Update time: 2024-12-05 16:28:37 +08:00 -# GitHub仓库: https://github.com/sinspired/cnNetTool +# Update time: 2024-12-09 14:15:15 +08:00 +# GitHubֿ: https://github.com/sinspired/cnNetTool # cnNetTool End diff --git a/requirements.txt b/requirements.txt index ee63f95..c30c2c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aiohttp==3.11.10 dnspython==2.7.0 -httpx==0.28.0 +httpx[http2]==0.28.0 prettytable==3.12.0 rich==13.9.4 wcwidth==0.2.13 diff --git a/setDNS.py b/setDNS.py index 31f8d9f..b875717 100644 --- a/setDNS.py +++ b/setDNS.py @@ -1,18 +1,19 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import argparse import ctypes +import logging import os -import sys -import threading -import dns.resolver import platform +import statistics import subprocess -import logging -import argparse +import sys +import threading import time -import statistics from concurrent.futures import ThreadPoolExecutor, as_completed + +import dns.resolver from prettytable import PrettyTable # 设置日志记录 @@ -317,7 +318,7 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): ], check=True, ) - for dns in ipv4_dns_list[1:]: + for dns_item in ipv4_dns_list[1:]: subprocess.run( [ "netsh", @@ -326,7 +327,7 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): "add", "dns", interface, - dns, + dns_item, "index=2", ], check=True, @@ -352,7 +353,7 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): ], check=True, ) - for dns in ipv6_dns_list[1:]: + for dns_item in ipv6_dns_list[1:]: subprocess.run( [ "netsh", @@ -361,7 +362,7 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): "add", "dns", interface, - dns, + dns_item, "index=2", ], check=True, @@ -371,12 +372,12 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): elif system == "Linux": with open("/etc/resolv.conf", "w") as f: - for dns in ipv4_dns_list: - logger.debug(f"添加IPv4 DNS到 /etc/resolv.conf: {dns}") - f.write(f"nameserver {dns}\n") - for dns in ipv6_dns_list: - logger.debug(f"添加IPv6 DNS到 /etc/resolv.conf: {dns}") - f.write(f"nameserver {dns}\n") + for dns_item in ipv4_dns_list: + logger.debug(f"添加IPv4 DNS到 /etc/resolv.conf: {dns_item}") + f.write(f"nameserver {dns_item}\n") + for dns_item in ipv6_dns_list: + logger.debug(f"添加IPv6 DNS到 /etc/resolv.conf: {dns_item}") + f.write(f"nameserver {dns_item}\n") elif system == "Darwin": # macOS all_dns = ipv4_dns_list + ipv6_dns_list dns_string = " ".join(all_dns) @@ -449,18 +450,21 @@ def print_recommended_dns_table(dns_list: list, ip_version: str, available_dns: table = PrettyTable() table.title = f"推荐的最佳{ip_version.upper()} DNS服务器" table.field_names = ["DNS", "提供商", "区域", "成功率", "平均延迟(ms)"] - for dns in dns_list: - if dns: + for dns_item in dns_list: + if dns_item: # 在best_dns列表中查找正确的服务器信息 server_info = next( - (info for server, - info in available_dns[ip_version] if server == dns), + ( + info + for server, info in available_dns[ip_version] + if server == dns_item + ), None, ) if server_info: table.add_row( [ - dns, + dns_item, server_info["provider"], server_info["region"], f"{server_info['success_rate']:.2%}", @@ -473,7 +477,7 @@ def print_recommended_dns_table(dns_list: list, ip_version: str, available_dns: def print_available_dns(available_dns, best_dns_num): print() - print(f"可用DNS服务器:") + print("可用DNS服务器:") for ip_version in ["ipv4", "ipv6"]: if available_dns[ip_version]: @@ -565,8 +569,7 @@ def main(): print_domain_resolutions(domain_resolutions, dns_performance) # 防止 best_dns_num 数值超过数组长度 - num_servers = max( - len(available_dns["ipv4"]), len(available_dns["ipv6"])) + num_servers = max(len(available_dns["ipv4"]), len(available_dns["ipv6"])) if args.best_dns_num > num_servers: args.best_dns_num = num_servers diff --git a/setHosts.py b/setHosts.py index 0fe1c92..f9d539e 100644 --- a/setHosts.py +++ b/setHosts.py @@ -1,30 +1,26 @@ -import os -import ssl -import sys -from pathlib import Path -import dns.resolver -import json -import shutil +import argparse import asyncio -import platform +import ctypes +import json import logging -import argparse -import aiohttp +import os +import platform +import re +import shutil import socket -from enum import Enum +import ssl +import sys from datetime import datetime, timedelta, timezone -from typing import List, Set, Optional, Dict, Tuple -import ctypes -import re +from enum import Enum from functools import wraps -from rich import print as rprint -from rich.progress import ( - Progress, - BarColumn, - TaskID, - TimeRemainingColumn, -) from math import floor +from pathlib import Path +from typing import Dict, List, Optional, Set, Tuple + +import aiohttp +import dns.resolver +from rich import print as rprint +from rich.progress import BarColumn, Progress, TaskID, TimeRemainingColumn # -------------------- 常量设置 -------------------- # RESOLVER_TIMEOUT = 1 # DNS 解析超时时间 秒 @@ -82,13 +78,12 @@ def parse_args(): help="打印运行信息", ) parser.add_argument( - "-n", - "--NotUseDnsServers", - action="store_true", - help="不使用DNS服务器解析(避免GitHub等被dns污染的网站获取错误地址)", + "-n", + "--NotUseDnsServers", + action="store_true", + help="不使用DNS服务器解析(避免GitHub等被dns污染的网站获取错误地址)", ) - return parser.parse_args() @@ -157,7 +152,7 @@ def write_readme_file( output_fb.write(readme_content) rprint( - f"[blue]已更新 README.md 文件,位于: [underline]README.md[/underline][/blue]\n" + "[blue]已更新 README.md 文件,位于: [underline]README.md[/underline][/blue]\n" ) except FileNotFoundError as e: @@ -704,7 +699,7 @@ def write_to_hosts_file(self, new_entries: List[str]): .replace("+0800", "+08:00") ) - rprint(f"\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") + rprint("\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") save_hosts_content = [] # 提取新内容文本 @@ -971,7 +966,6 @@ async def _process_domain_group(self, group: DomainGroup, index: int) -> List[st async def update_hosts(self): """主更新函数,支持并发进度显示""" - cache_valid = self.resolver._is_dns_cache_valid() with self.progress: # 并发处理所有组 diff --git a/setHosts_Classic.py b/setHosts_Classic.py index ba6c323..0f946f4 100644 --- a/setHosts_Classic.py +++ b/setHosts_Classic.py @@ -1,28 +1,30 @@ -import os -import ssl -import sys -from pathlib import Path +import argparse +import asyncio import concurrent -import dns.resolver +import ctypes import json -import shutil -import asyncio -import platform import logging -import argparse +import logging.config +import os +import platform +import re +import shutil import socket -from enum import Enum +import ssl +import sys from datetime import datetime, timedelta, timezone -from typing import List, Set, Optional, Dict, Tuple -import ctypes -import re +from enum import Enum from functools import wraps +from math import floor +from pathlib import Path +from typing import Dict, List, Optional, Set, Tuple + +import dns.resolver import httpx -from rich import print as rprint -from rich.progress import Progress, SpinnerColumn, TextColumn import wcwidth -from math import floor -import logging.config +from rich import print as rprint + +# from rich.progress import Progress, SpinnerColumn, TextColumn # -------------------- 常量设置 -------------------- # RESOLVER_TIMEOUT = 0.1 # DNS 解析超时时间 秒 @@ -122,8 +124,7 @@ def backup_hosts_file(hosts_file_path: str): backup_path = f"{hosts_file_path}.bak" shutil.copy(hosts_file_path, backup_path) rprint( - f"\n[blue]已备份 [underline]{ - hosts_file_path}[/underline] 到 [underline]{backup_path}[/underline][/blue]" + f"\n[blue]已备份 [underline]{hosts_file_path}[/underline] 到 [underline]{backup_path}[/underline][/blue]" ) @staticmethod @@ -155,15 +156,14 @@ def write_readme_file( # 使用替换方法而不是format readme_content = template_content.replace("{hosts_str}", hosts_str) - readme_content = readme_content.replace( - "{update_time}", update_time) + readme_content = readme_content.replace("{update_time}", update_time) # 写入新文件 with open("README.md", "w", encoding="utf-8") as output_fb: output_fb.write(readme_content) rprint( - f"[blue]已更新 README.md 文件,位于: [underline]README.md[/underline][/blue]\n" + "[blue]已更新 README.md 文件,位于: [underline]README.md[/underline][/blue]\n" ) except FileNotFoundError as e: @@ -214,12 +214,10 @@ def get_formatted_output(text, fill_char=".", align_position=0.97): main_text = plain_text.strip() completion_mark = "[完成]" # 关键修改:直接从目标宽度减去主文本长度,不再额外预留[完成]的空间 - fill_count = target_width - \ - len(main_text) - len(completion_mark) - 6 + fill_count = target_width - len(main_text) - len(completion_mark) - 6 fill_count = max(0, fill_count) - filled_text = f"{main_text}{ - fill_char * fill_count}{completion_mark}" + filled_text = f"{main_text}{fill_char * fill_count}{completion_mark}" return f"[blue on green]{filled_text}[/blue on green]" else: # 普通文本的处理保持不变 @@ -261,8 +259,7 @@ def get_align_str( padding_needed = max(0, padding_needed) # 构建最终的格式化字符串 - formatted_str = f"\n[bold white on bright_black]正在处理第 [green]{ - i}[/green] 组域名: {group_name.upper()}{' ' * padding_needed}[/bold white on bright_black]" + formatted_str = f"\n[bold white on bright_black]正在处理第 [green]{i}[/green] 组域名: {group_name.upper()}{' ' * padding_needed}[/bold white on bright_black]" return formatted_str @@ -355,8 +352,7 @@ async def resolve_domain(self, domain: str) -> Set[str]: ips.update(ipv4_ips + ipv6_ips) logging.debug( - f"成功通过缓存文件解析 {domain}, 发现 {len( - ipv4_ips)+len(ipv6_ips)} 个 DNS 主机:\n{ipv4_ips}\n{ipv6_ips if ipv6_ips else ''}\n" + f"成功通过缓存文件解析 {domain}, 发现 {len(ipv4_ips) + len(ipv6_ips)} 个 DNS 主机:\n{ipv4_ips}\n{ipv6_ips if ipv6_ips else ''}\n" ) else: ipaddress_ips = await self._resolve_via_ipaddress(domain) @@ -365,8 +361,7 @@ async def resolve_domain(self, domain: str) -> Set[str]: if ips: logging.debug( - f"成功通过 DNS服务器 和 DNS记录 解析 {domain}, 发现 { - len(ips)} 个 唯一 DNS 主机\n{ips}\n" + f"成功通过 DNS服务器 和 DNS记录 解析 {domain}, 发现 {len(ips)} 个 唯一 DNS 主机\n{ips}\n" ) else: logging.debug(f"警告: 无法解析 {domain}") @@ -376,9 +371,9 @@ async def resolve_domain(self, domain: str) -> Set[str]: total_resolve_duration = ipaddress_resolve_end_time - start_time logging.debug( - f"IP地址解析耗时: {ipaddress_resolve_duration.total_seconds():.2f}秒") - logging.debug( - f"DNS解析总耗时: {total_resolve_duration.total_seconds():.2f}秒") + f"IP地址解析耗时: {ipaddress_resolve_duration.total_seconds():.2f}秒" + ) + logging.debug(f"DNS解析总耗时: {total_resolve_duration.total_seconds():.2f}秒") return ips @@ -419,8 +414,7 @@ async def resolve_with_dns_server(dns_server_info: dict) -> Set[str]: if ips: logging.debug( - f"成功使用 {dns_provider} : {dns_server} 解析 { - domain},共 {len(ips)} 个主机: {ips}" + f"成功使用 {dns_provider} : {dns_server} 解析 {domain},共 {len(ips)} 个主机: {ips}" ) return ips @@ -431,27 +425,29 @@ async def resolve_with_dns_server(dns_server_info: dict) -> Set[str]: # 根据 dns_type 选择要使用的 DNS 服务器 if dns_type.lower() == "all": - dns_servers = self.dns_servers['china_mainland'] + \ - self.dns_servers['international'] + dns_servers = ( + self.dns_servers["china_mainland"] + self.dns_servers["international"] + ) elif dns_type.lower() == "china": - dns_servers = self.dns_servers['china_mainland'] + dns_servers = self.dns_servers["china_mainland"] elif dns_type.lower() == "global" or dns_type.lower() == "international": - dns_servers = self.dns_servers['international'] + dns_servers = self.dns_servers["international"] else: - dns_servers = self.dns_servers['china_mainland'] + \ - self.dns_servers['international'] + dns_servers = ( + self.dns_servers["china_mainland"] + self.dns_servers["international"] + ) # raise ValueError(f"无效的 DNS 类型:{dns_type}") # 并发解析所有选定的 DNS 服务器,并保留非空结果 - tasks = [resolve_with_dns_server(dns_server) - for dns_server in dns_servers] + tasks = [resolve_with_dns_server(dns_server) for dns_server in dns_servers] results = await asyncio.gather(*tasks) # 合并所有非空的解析结果 ips = set(ip for result in results for ip in result if ip) if ips: - logging.debug(f"成功使用多个 DNS 服务器解析 {domain},共 { - len(ips)} 个主机:\n{ips}\n") + logging.debug( + f"成功使用多个 DNS 服务器解析 {domain},共 {len(ips)} 个主机:\n{ips}\n" + ) # input("按任意键继续") return ips @@ -463,7 +459,7 @@ async def wrapper(*args, **kwargs): for attempt in range(tries): try: return await func(*args, **kwargs) - except Exception as e: + except Exception: if attempt < tries - 1: print(f"第 {attempt + 2} 次尝试:") # logging.debug(f"通过DNS_records解析 {args[1]},第 {attempt + 2} 次尝试:") @@ -479,8 +475,7 @@ async def wrapper(*args, **kwargs): } self.save_hosts_cache() logging.warning( - f"ipaddress.com {tries} 次尝试后未解析到 { - domain} 的 DNS_records 地址," + f"ipaddress.com {tries} 次尝试后未解析到 {domain} 的 DNS_records 地址," f"已写入空地址到缓存以免无谓消耗网络资源" ) # print(f"通过 DNS_records 解析 { @@ -488,7 +483,9 @@ async def wrapper(*args, **kwargs): return None await asyncio.sleep(delay) return None + return wrapper + return decorator LOGGING_CONFIG = { @@ -497,7 +494,7 @@ async def wrapper(*args, **kwargs): "httpxHandlers": { "class": "logging.StreamHandler", "formatter": "http", - "stream": "ext://sys.stderr" + "stream": "ext://sys.stderr", } }, "formatters": { @@ -506,16 +503,16 @@ async def wrapper(*args, **kwargs): "datefmt": "%Y-%m-%d %H:%M:%S", } }, - 'loggers': { - 'httpx': { - 'handlers': ['httpxHandlers'], - 'level': 'WARNING', + "loggers": { + "httpx": { + "handlers": ["httpxHandlers"], + "level": "WARNING", }, - 'httpcore': { - 'handlers': ['httpxHandlers'], - 'level': 'WARNING', + "httpcore": { + "handlers": ["httpxHandlers"], + "level": "WARNING", }, - } + }, } logging.config.dictConfig(LOGGING_CONFIG) @@ -524,10 +521,14 @@ async def wrapper(*args, **kwargs): async def _resolve_via_ipaddress(self, domain: str) -> Set[str]: ips = set() url = f"https://www.ipaddress.com/website/{domain}" + # headers = { + # "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + # "AppleWebKit/537.36 (KHTML, like Gecko) " + # "Chrome/106.0.0.0 Safari/537.36" + # } headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/106.0.0.0 Safari/537.36" + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.121 Safari/537.36", + "Referer": "https://www.ipaddress.com", } try: @@ -563,8 +564,9 @@ async def _resolve_via_ipaddress(self, domain: str) -> Set[str]: "source": "DNS_records", } self.save_hosts_cache() - logging.debug(f"通过 ipaddress.com 成功解析 { - domain} 并更新 DNS_records 缓存") + logging.debug( + f"通过 ipaddress.com 成功解析 {domain} 并更新 DNS_records 缓存" + ) logging.debug(f"DNS_records:\n {ips}") else: self.dns_records[domain] = { @@ -574,13 +576,15 @@ async def _resolve_via_ipaddress(self, domain: str) -> Set[str]: "source": "DNS_records", } self.save_hosts_cache() - logging.warning(f"ipaddress.com 未解析到 { - domain} 的 DNS_records 地址,已写入空地址到缓存以免无谓消耗网络资源") + logging.warning( + f"ipaddress.com 未解析到 {domain} 的 DNS_records 地址,已写入空地址到缓存以免无谓消耗网络资源" + ) except Exception as e: logging.error(f"通过DNS_records解析 {domain} 失败! {e}") raise return ips + # -------------------- 延迟测速模块 -------------------- # @@ -602,39 +606,43 @@ async def get_lowest_latency_hosts( all_ips = list(file_ips) # start_time = datetime.now() rprint( - f"[bright_black]- 获取到 [bold bright_green]{ - len(all_ips)}[/bold bright_green] 个唯一IP地址[/bright_black]" + f"[bright_black]- 获取到 [bold bright_green]{len(all_ips)}[/bold bright_green] 个唯一IP地址[/bright_black]" ) if all_ips: - rprint(f"[bright_black]- 检测主机延迟...[/bright_black]") + rprint("[bright_black]- 检测主机延迟...[/bright_black]") # 使用线程池来并发处理SSL证书验证 - with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor: + with concurrent.futures.ThreadPoolExecutor( + max_workers=self.max_workers + ) as executor: # 第一步:并发获取IP延迟 ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] latency_results = await asyncio.gather(*ping_tasks) # 筛选有效延迟的IP valid_latency_results = [ - result for result in latency_results - if result[1] != float("inf") + result for result in latency_results if result[1] != float("inf") ] if valid_latency_results: if len(valid_latency_results) < len(all_ips): rprint( - f"[bright_black]- 检测到 [bold bright_green]{ - len(valid_latency_results)}[/bold bright_green] 个有效IP地址[/bright_black]" + f"[bright_black]- 检测到 [bold bright_green]{len(valid_latency_results)}[/bold bright_green] 个有效IP地址[/bright_black]" ) valid_latency_ips = [ - result for result in valid_latency_results if result[1] < latency_limit] + result + for result in valid_latency_results + if result[1] < latency_limit + ] if not valid_latency_ips: logging.warning(f"未发现延迟小于 {latency_limit}ms 的IP。") - min_result = [ - min(valid_latency_results, key=lambda x: x[1])] - latency_limit = min_result[0][1]*2 + min_result = [min(valid_latency_results, key=lambda x: x[1])] + latency_limit = min_result[0][1] * 2 logging.debug(f"主机IP最低延迟 {latency_limit:.0f}ms") valid_latency_ips = [ - result for result in valid_latency_results if result[1] <= latency_limit] + result + for result in valid_latency_results + if result[1] <= latency_limit + ] else: rprint("[red]延迟检测没有获得有效IP[/red]") return [] @@ -644,21 +652,18 @@ async def get_lowest_latency_hosts( if len(valid_latency_ips) < len(valid_latency_results): rprint( - f"[bright_black]- 检测到 [bold bright_green]{ - len(valid_latency_ips)}[/bold bright_green] 个延迟小于 {latency_limit}ms 的有效IP地址[/bright_black]" + f"[bright_black]- 检测到 [bold bright_green]{len(valid_latency_ips)}[/bold bright_green] 个延迟小于 {latency_limit}ms 的有效IP地址[/bright_black]" ) - ipv4_results = [ - r for r in valid_latency_ips if not Utils.is_ipv6(r[0])] - ipv6_results = [ - r for r in valid_latency_ips if Utils.is_ipv6(r[0])] + ipv4_results = [r for r in valid_latency_ips if not Utils.is_ipv6(r[0])] + ipv6_results = [r for r in valid_latency_ips if Utils.is_ipv6(r[0])] # 第二步:使用线程池并发验证SSL证书 # if "github" in group_name.lower(): - if len(valid_latency_ips) > 1 and any(keyword in group_name.lower() for keyword in ["google"]): - rprint( - f"[bright_black]- 验证SSL证书...[/bright_black]" - ) + if len(valid_latency_ips) > 1 and any( + keyword in group_name.lower() for keyword in ["google"] + ): + rprint("[bright_black]- 验证SSL证书...[/bright_black]") ipv4_count = 0 ipv6_count = 0 batch_size = args.batch_size @@ -669,14 +674,14 @@ async def get_lowest_latency_hosts( for i in range(0, total_results, batch_size): min_len = min(total_results, batch_size) - batch = valid_latency_ips[i:i + min_len] + batch = valid_latency_ips[i : i + min_len] ssl_verification_tasks = [ loop.run_in_executor( executor, self._sync_is_cert_valid_dict, domains[0], ip, - latency + latency, ) for ip, latency in batch ] @@ -735,8 +740,7 @@ async def get_host_average_latency( ) response_times = [t for t in response_times if t != float("inf")] if response_times: - average_response_time = sum( - response_times) / len(response_times) + average_response_time = sum(response_times) / len(response_times) else: average_response_time = float("inf") @@ -778,7 +782,9 @@ async def get_latency(self, ip: str, port: int = 443) -> float: logging.error(f"获取地址信息失败 {ip}: {e}") return float("inf") - def _sync_is_cert_valid_dict(self, domain: str, ip: str, latency: float, port: int = 443) -> Tuple[str, float, bool]: + def _sync_is_cert_valid_dict( + self, domain: str, ip: str, latency: float, port: int = 443 + ) -> Tuple[str, float, bool]: """ 同步版本的证书验证方法,用于在线程池中执行 """ @@ -791,25 +797,29 @@ def _sync_is_cert_valid_dict(self, domain: str, ip: str, latency: float, port: i with context.wrap_socket(sock, server_hostname=domain) as ssock: cert = ssock.getpeercert() not_after = datetime.strptime( - cert["notAfter"], "%b %d %H:%M:%S %Y %Z") + cert["notAfter"], "%b %d %H:%M:%S %Y %Z" + ) if not_after < datetime.now(): - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 证书已过期") + logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书已过期") return (ip, latency, False) - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: SSL证书有效,截止日期为 {not_after}") + logging.debug( + f"{domain} ({ip}) {latency:.0f}ms: SSL证书有效,截止日期为 {not_after}" + ) return (ip, latency, True) except ConnectionError as e: - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}") + logging.debug( + f"{domain} ({ip}) {latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}" + ) return (ip, latency, True) except Exception as e: logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书验证失败 - {e}") return (ip, latency, False) - def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: float, port: int = 443) -> Tuple[str, float, bool]: + def _sync_is_cert_valid_dict_average( + self, domains: List[str], ip: str, latency: float, port: int = 443 + ) -> Tuple[str, float, bool]: """ 同步版本的证书验证方法,用于在线程池中执行。 任意一个 domain 验证通过就视为通过。 @@ -824,19 +834,23 @@ def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: with context.wrap_socket(sock, server_hostname=domain) as ssock: cert = ssock.getpeercert() not_after = datetime.strptime( - cert["notAfter"], "%b %d %H:%M:%S %Y %Z") + cert["notAfter"], "%b %d %H:%M:%S %Y %Z" + ) if not_after < datetime.now(): - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 证书已过期") + logging.debug( + f"{domain} ({ip}) {latency:.0f}ms: 证书已过期" + ) continue # 检查下一个 domain - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: SSL证书有效,截止日期为 {not_after}") + logging.debug( + f"{domain} ({ip}) {latency:.0f}ms: SSL证书有效,截止日期为 {not_after}" + ) return (ip, latency, True) # 任意一个验证通过即返回成功 except ConnectionError as e: - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}") + logging.debug( + f"{domain} ({ip}) {latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}" + ) return (ip, latency, True) except Exception as e: logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书验证失败 - {e}") @@ -845,7 +859,9 @@ def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: # 如果所有 domain 都验证失败 return (ip, latency, False) - def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tuple[str, float]]: + def _select_best_hosts( + self, valid_results: List[Tuple[str, float]] + ) -> List[Tuple[str, float]]: """ 选择最佳主机,优先考虑IPv4和IPv6 """ @@ -863,12 +879,14 @@ def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tup for ip, latency in ipv4_results: best_hosts.append((ip, latency)) selected_count += 1 - if (ipv6_results and selected_count >= 1) or selected_count >= self.hosts_num: + if ( + ipv6_results and selected_count >= 1 + ) or selected_count >= self.hosts_num: break # 再选择IPv6 if ipv6_results: for ip, latency in ipv6_results: - if ipv4_results and latency <= min_ipv4_results[1]*2: + if ipv4_results and latency <= min_ipv4_results[1] * 2: best_hosts.append((ip, latency)) break else: @@ -881,17 +899,20 @@ def _print_results(self, best_hosts: List[Tuple[str, float]], latency_limit: int """ 打印结果的方法 """ - rprint(f"[bold yellow]最快的 DNS主机 IP(优先选择 IPv6) 丨 延迟 < { - latency_limit:.0f}ms :[/bold yellow]") + rprint( + f"[bold yellow]最快的 DNS主机 IP(优先选择 IPv6) 丨 延迟 < {latency_limit:.0f}ms :[/bold yellow]" + ) for ip, time in best_hosts: - rprint(f" [green]{ - ip}[/green] [bright_black]{time:.2f} ms[/bright_black]") + rprint( + f" [green]{ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" + ) # end_time = datetime.now() # total_time = end_time - start_time # rprint( # f"[bright_black]- 运行时间:[/bright_black] [cyan]{total_time.total_seconds():.2f} 秒[/cyan]") + # -------------------- Hosts文件管理 -------------------- # @@ -957,7 +978,7 @@ def write_to_hosts_file(self, new_entries: List[str]): .replace("+0800", "+08:00") ) - rprint(f"\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") + rprint("\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") save_hosts_content = [] # 提取新内容文本 @@ -1014,8 +1035,7 @@ def write_to_hosts_file(self, new_entries: List[str]): with open("hosts", "w") as f: f.write("\n".join(save_hosts_content)) rprint( - f"\n[blue]已生成 hosts 文件,位于: [underline]hosts[/underline][/blue] (共 { - len(new_entries)} 个条目)" + f"\n[blue]已生成 hosts 文件,位于: [underline]hosts[/underline][/blue] (共 {len(new_entries)} 个条目)" ) if not getattr(sys, "frozen", False): @@ -1057,8 +1077,7 @@ async def update_hosts(self): all_ips = set() if default_ips: rprint( - f"[bright_black]- 读取到 [bold bright_green]{ - len(default_ips)}[/bold bright_green] 个预设IP地址[/bright_black]" + f"[bright_black]- 读取到 [bold bright_green]{len(default_ips)}[/bold bright_green] 个预设IP地址[/bright_black]" ) all_ips.update(default_ips) @@ -1080,25 +1099,25 @@ async def update_hosts(self): logging.warning(f"{domain} 未找到延迟满足要求的IP。") continue - new_entries = [f"{ip}\t{domain}" for ip, - latency in fastest_ips] + new_entries = [f"{ip}\t{domain}" for ip, latency in fastest_ips] all_entries.extend(new_entries) else: all_ips = set() if default_ips: rprint( - f"[bright_black]- 读取到 [bold bright_green]{ - len(default_ips)}[/bold bright_green] 个预设IP地址[/bright_black]" + f"[bright_black]- 读取到 [bold bright_green]{len(default_ips)}[/bold bright_green] 个预设IP地址[/bright_black]" ) all_ips.update(default_ips) # 收集组内所有域名的DNS解析结果 - domain_resolve_tasks = [self.resolver.resolve_domain( - domain) for domain in group.domains] - resolved_ips = await asyncio.gather(*domain_resolve_tasks, return_exceptions=True) + domain_resolve_tasks = [ + self.resolver.resolve_domain(domain) for domain in group.domains + ] + resolved_ips = await asyncio.gather( + *domain_resolve_tasks, return_exceptions=True + ) - all_ips.update( - ip for ip_list in resolved_ips for ip in ip_list if ip) + all_ips.update(ip for ip_list in resolved_ips for ip in ip_list if ip) if not all_ips: logging.warning(f"组 {group.name} 未找到任何可用IP。跳过该组。") @@ -1120,8 +1139,7 @@ async def update_hosts(self): f"\n[bold]为组 {group.name} 内所有域名应用延迟最低的 DNS 映射主机IP:[/bold]" ) for domain in group.domains: - new_entries = [f"{ip}\t{domain}" for ip, - latency in fastest_ips] + new_entries = [f"{ip}\t{domain}" for ip, latency in fastest_ips] rprint(f"[bright_black] - {domain}[/bright_black]") all_entries.extend(new_entries) @@ -1612,7 +1630,6 @@ class Config: "185.199.110.133", "185.199.111.133", }, - ), DomainGroup( name="JetBrain 插件", @@ -1630,74 +1647,39 @@ class Config: "international": [ # 国际 DNS 服务器 # 第 1 梯队: 延迟较低 - { - "ip": "208.67.222.222", # Open DNS - "provider": "OpenDNS", - "type": "ipv4" - }, - { - "ip": "2620:0:ccc::2", # Open DNS - "provider": "OpenDNS", - "type": "ipv6" - }, + {"ip": "208.67.222.222", "provider": "OpenDNS", "type": "ipv4"}, # Open DNS + {"ip": "2620:0:ccc::2", "provider": "OpenDNS", "type": "ipv6"}, # Open DNS { "ip": "2001:4860:4860::8888", # Google Public DNS "provider": "Google", - "type": "ipv6" + "type": "ipv6", }, { "ip": "2001:4860:4860::8844", # Google Public DNS "provider": "Google", - "type": "ipv6" - }, - { - "ip": "210.184.24.65", - "provider": "CPC HK", # 香港 - "type": "ipv4" - }, - { - "ip": "18.163.103.200", - "provider": "Amazon HK", # 香港 - "type": "ipv4" + "type": "ipv6", }, + {"ip": "210.184.24.65", "provider": "CPC HK", "type": "ipv4"}, # 香港 + {"ip": "18.163.103.200", "provider": "Amazon HK", "type": "ipv4"}, # 香港 { "ip": "43.251.159.130", "provider": "IPTELECOM HK", # 香港 - "type": "ipv4" + "type": "ipv4", }, { "ip": "14.198.168.140", "provider": "Broadband HK", # 香港 - "type": "ipv4" + "type": "ipv4", }, { "ip": "66.203.146.122", "provider": "Dimension HK", # 香港 - "type": "ipv4" - }, - { - "ip": "118.201.189.90", - "provider": "SingNet", # 新加坡 - "type": "ipv4" + "type": "ipv4", }, - { - "ip": "1.228.180.5", - "provider": "SK Broadband ", # 韩国 - "type": "ipv4" - }, - { - "ip": "183.99.33.6", - "provider": "Korea Telecom ", # 韩国 - "type": "ipv4" - }, - { - "ip": "203.248.252.2", - "provider": "LG DACOM ", # 韩国 - "type": "ipv4" - }, - - - + {"ip": "118.201.189.90", "provider": "SingNet", "type": "ipv4"}, # 新加坡 + {"ip": "1.228.180.5", "provider": "SK Broadband ", "type": "ipv4"}, # 韩国 + {"ip": "183.99.33.6", "provider": "Korea Telecom ", "type": "ipv4"}, # 韩国 + {"ip": "203.248.252.2", "provider": "LG DACOM ", "type": "ipv4"}, # 韩国 # 第 2 梯队:延迟适中 # { # "ip": "129.250.35.250", @@ -1709,16 +1691,11 @@ class Config: # "provider": "KT DNS", # 韩国 # "type": "ipv4" # }, - - - - # { # "ip": "101.110.50.106", # "provider": "Soft Bank", # "type": "ipv4" # }, - # { # "ip": "202.175.86.206", # "provider": "Telecomunicacoes de Macau", #澳门 @@ -1729,20 +1706,16 @@ class Config: # "provider": "Informacoes Tecnologia de Macau", #澳门 # "type": "ipv4" # }, - - # { # "ip": "2400:6180:0:d0::5f6e:4001", # "provider": "DigitalOcean", # 新加坡 # "type": "ipv6" # }, - # { # "ip": "2a09::", # DNS.SB 德国 2a11:: # "provider": "DNS.SB", # "type": "ipv6" # }, - # { # "ip": "185.222.222.222", # DNS.SB 德国45.11.45.11 # "provider": "DNS.SB", @@ -1753,8 +1726,6 @@ class Config: # "provider": "Quad9", # "type": "ipv4" # }, - - # { # "ip": "149.112.112.112", # Quad9 DNS # "provider": "Quad9", @@ -1800,7 +1771,6 @@ class Config: # "provider": "CIRA Canadian Shield",# 加拿大 # "type": "ipv6" # }, - # { # "ip": "77.88.8.1", # "provider": "Yandex DNS",# 俄国 @@ -1816,18 +1786,12 @@ class Config: # 国内 DNS 服务器 # 第 1 梯队:正确解析Google翻译 # 首选:延迟较低,相对稳定: - { - "ip": "114.114.114.114", # 114 DNS - "provider": "114DNS", - "type": "ipv4" - }, + {"ip": "114.114.114.114", "provider": "114DNS", "type": "ipv4"}, # 114 DNS { "ip": "1.1.8.8", # 上海牙木科技|联通机房 "provider": "上海牙木科技|联通机房", - "type": "ipv4" + "type": "ipv4", }, - - # 备选:延迟一般: # { # "ip": "180.76.76.76", # 百度 @@ -1844,16 +1808,12 @@ class Config: # "provider": "Shanghai Communications", # "type": "ipv4" # },240c::6644 - - - # 第 2 梯队:无法正确解析Google翻译 # { # "ip": "223.5.5.5", # 阿里云 DNS # "provider": "Alibaba", # "type": "ipv4" # }, - # { # "ip": "2400:3200::1", # 阿里云 DNS # "provider": "Alibaba",