├── .gitignore ├── LICENSE ├── README.md ├── README_EN.md ├── clashauto ├── clashauto-win.py ├── clashauto.bat ├── clashautoutil.py ├── data ├── basic_clash_config.yaml ├── config.ini ├── profiles │ └── example_url └── tpl │ ├── proxy_provider_urls │ ├── tpl_basic_multi_pp.yaml │ ├── tpl_basic_single_pp.yaml │ ├── tpl_chatgpt_multi_pp.yaml │ └── tpl_chatgpt_single_pp.yaml ├── final_clash_config.yaml └── screenshots └── clashauto.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 JohanChane 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 be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clash Auto 2 | 3 | ## **该项目不再维护, 请使用 [clashtui](https://github.com/JohanChane/clashtui) 替代。** 4 | 5 | language: [English (out of date)](./README_EN.md) | [中文](./README.md) 6 | 7 | ## 目录 8 | 9 | 10 | 11 | * [开发此软件的原因](#开发此软件的原因) 12 | * [特点](#特点) 13 | * [支持的平台](#支持的平台) 14 | * [安装](#安装) 15 | * [使用](#使用) 16 | * [日常使用](#日常使用) 17 | * [导入机场链接](#导入机场链接) 18 | * [更新机场链接](#更新机场链接) 19 | * [clash 的客户端](#clash-的客户端) 20 | * [网站无法访问 (不常见的场景)](#网站无法访问-不常见的场景) 21 | * [软件的文件的作用](#软件的文件的作用) 22 | * [clashauto.bat 的选项](#clashautobat-的选项) 23 | * [Clash Auto 的配置](#clash-auto-的配置) 24 | * [根据模板配置生成新的配置](#根据模板配置生成新的配置) 25 | * [更新 ClashAuto](#更新-clashauto) 26 | * [Linux](#linux) 27 | * [依赖](#依赖) 28 | * [安装](#安装-1) 29 | * [使用](#使用-1) 30 | * [Screenshots](#screenshots) 31 | 32 | 33 | 34 | ## 开发此软件的原因 35 | 36 | 1. 我一般有多个机场链接, 因为有些机场并不能保证 24 小时能上网。所以喜欢将多个机场写在一个配置文件, 方便切换。 37 | 2. 可以用自己的分流规则。这样做会更加安全。再整个黑白名单模式切换。 38 | - 黑名单模式好处是省流量, 安全 (不需要走代理的网站不走代理)。坏处是遇到不匹配的网站要切换到白名单模式。 39 | - 白名单模式的好处是规则如果没有匹配到该网站时也可以访问, 不用切换模式。坏处是不省流量, 可以不走代理的网站走了代理, 不安全。 40 | 3. 自己写的配置一般会有依赖的网络资源 (比如: `url` 字段) 时, 所以可能会出现这种情况: 41 | - [Clash 更新这些资源是不走代理的。](https://github.com/Dreamacro/clash/issues/2368) 42 | - [[Feature] 让providers可以通过代理来更新](https://github.com/Dreamacro/clash/issues/2046) 43 | - [windows11 error](https://github.com/Fndroid/clash_for_windows_pkg/issues/2384) 44 | - [[Feature] rule-providers 和 proxy-providers 能不能提供选择直连或代理选项](https://github.com/Dreamacro/clash/issues/1385) 45 | 46 | 我根据自己平时的使用, 开发了这个软件实现了上述的功能和解决了上述的问题。如果你使用 clash 的习惯和我相同, 这个软件应该适合你。 47 | 48 | ## 特点 49 | 50 | - 通过自身代理来更新配置而不是直连。 51 | - 可定义配置模块来生成 Clash 配置。对于有多个订阅链接的用户可以将所有订阅合并到一个配置文件, 而且订阅之间不混乱。 52 | - 可自定义后端地址将订阅链接转换为 Clash 配置。 53 | - 小巧高效。平时只需运行一个 Clash Server 在后台。ClashAuto 要用时再打开即可。 54 | 55 | ## 支持的平台 56 | 57 | - Windows 58 | - Linux 59 | 60 | ## 安装 61 | 62 | 1. 从 [release](https://github.com/JohanChane/clash-auto/releases) 下载软件包 (免安装)。 63 | 2. 双击运行 `clashauto.bat`。 64 | 3. 选择 `install` 后, Windows 会安装一个开机启动的 clash 服务。 65 | 4. 选择 `test_config`, 让 clash 下载所需要的文件, 比如: `Country.mmdb`。 66 | 67 | *手动允许 `clash.exe` 程序通过防火墙。(如果客户端可以访问 clash 服务, 但是不能访问外网的情况下。)* 68 | 69 | ## 使用 70 | 71 | ### 日常使用 72 | 73 | #### 导入机场链接 74 | 75 | 如果只有一个机场 (有两种方式): 76 | - 在 `data/profiles` 目录下新建一个后缀为 `_url` 的文件, 将机场链接放入其中。然后更新并选择这个配置即可。 77 | - 将机场链接放入 `data/tpl/proxy_provider_urls` 中, 然后使用 `create_yaml` 的 `tpl_basic_single_pp.yaml` 模板创建 clash 配置文件。接下来更新并选择生成的配置即可。 78 | 79 | 如果有两个以上的机场: 80 | - 将机场链接放入 `data/tpl/proxy_provider_urls` 中, 然后使用 `create_yaml` 的 `tpl_basic_multi_pp.yaml` 模板创建 clash 配置文件。接下来更新并选择生成的配置即可。 81 | 82 | #### 更新机场链接 83 | 84 | - 如果导入机场链接的方式是, 在 `data/profiles` 目录下新建一个后缀为 `_url` 的文件的方式的, 更新并选择该配置文件即可。 85 | - 如果导入机场链接的方式是使用模板的, 选择 `update_final_config` 选项即可。 86 | 87 | #### clash 的客户端 88 | 89 | 在浏览器打开 `http://127.0.0.1:9090/ui`。(建议固定该标签页, 这样每次打开浏览器时会自动打开该标签页。) 90 | 91 | #### 网站无法访问 (不常见的场景) 92 | 93 | 假设使用模板生成的配置: 94 | - 如果只是临时访问该网站, 则可以在 `yacd` 中的 `Entry-RuleMode` 选择 `Entry` (确保旧链接已经断开)。访问完之后, 记得切回原来的选项。 95 | - 如果是经常要访问该网站, 则可以在模板中为该网站添加 [clash 分流规则](https://dreamacro.github.io/clash/zh_CN/configuration/rules.html#rules-%E8%A7%84%E5%88%99) (一般使用 `DOMAIN-SUFFIX, DOMAIN, DOMAIN-KEYWORD` 即可)。然后使用该模板重新生成的配置。 96 | 97 | ### 软件的文件的作用 98 | 99 | - clashauto.bat: 启动 Windows 平台的 ClashAuto 100 | - clashauto: 启动 Linux 平台的 ClashAuto 101 | - data: 放置用户的数据。 102 | - config.ini: ClashAuto 的配置。 103 | - profiles: 用于放置 profile 文件。 104 | - basic_clash_config.yaml: 用于配置 clash 的基础配置。修改该文件后, 记得重启 clashauto.bat 105 | - tpl: 用于根据 Clash 模板配置文件生成新的配置文件。 106 | - proxy_provider_urls: 放置有 proxies 字段的 Clash 配置。 107 | - .yaml 文件: Clash 模板配置文件 108 | - tpl_basic_single_pp.yaml: 如果只有一个 provider, 可以使用此模板。 109 | - tpl_basic_multi_pp.yaml: 如果有多个 provider, 可以使用此模板。 110 | - final_clash_config.yaml: 通过 basic_clash_config 和 profile 文件合并后配置文件, clash 是用这个配置文件启动的。 111 | - clash_config: 是 clash 的配置目录。 112 | 113 | ### clashauto.bat 的选项 114 | 115 | 双击运行 `clashauto.bat` 后, 会有如下选项: 116 | 117 | - update_final_config: 表示更新 final_clash_config.yaml 这个文件依赖的资源。 118 | - update_profile 119 | 120 | 表示更新 profile 文件依赖的资源。 121 | 122 | 可以用一个后缀是 `_url` 的文件放置 profile 的链接。它会将链接的内容保存到将 `_url` 为 `.yaml` 的文件。同时会更新该 profile 依赖的资源。 123 | 124 | 如果链接的内容不是一个 clash 配置文件, 可以通过一些订阅转换网站转换成 clash 的配置。比如: https://acl4ssr-sub.github.io/ 125 | 126 | 比如: 127 | 128 | profiles/example_url 129 | 130 | ``` 131 | 132 | ``` 133 | 134 | 同时也支持 `vmess://, trojan://` 等开头的链接并且可以将它们一起放在一个 url 文件。比如: 135 | 136 | profiles/example_url 137 | 138 | ``` 139 | 140 | vmess://... 141 | trojan://... 142 | ``` 143 | 144 | 选择该选项后会将 url 的内容保存到 profiles/example.yaml, 且更新该配置依赖的资源。更新成功后, 用户在 select_profile 选项中选择该配置即可。 145 | 146 | - select_profile: 表示选择一个 `profile` 和 `basic_clash_config.yaml` 合并到 `final_clash_config.yaml`。同时重启 clash 服务使配置文件生效。 147 | - restart/stop/config/install/uninstall clash_service: 都是用于操作 clash 服务。 148 | - test_config: 测试 `final_clash_config.yaml` 配置文件。 149 | - create_yaml: 用于根据 Clash 模板配置文件生成新的配置文件。 150 | - tun_mode: 启用/关闭 tun 模式 151 | - uwp_loopback: 允许应用程序在本地回环地址(loopback address)上进行网络通信。为了增强应用程序的安全性,Microsoft 在默认情况下禁用了微软商店的应用在本地回环地址上进行网络通信的功能。 152 | 153 | *Clash Auto 会使用自身作为代理来更新 clash 配置文件的依赖和更新 clash 的配置文件。* 154 | 155 | ### Clash Auto 的配置 156 | 157 | sc_host: 表示订阅转换的后端地址。在转换 url 时, 如果发现 url 的内容不是 Clash 配置, 则使用订阅转换来转换该 url。 158 | tun_mode: 表示启动/关闭 tun 模式。 159 | 160 | ### 根据模板配置生成新的配置 161 | 162 | 目的: 如何有多个有 proxies 字段的配置合并在一个配置文件中使用时, 一般会为每个配置文件写一个 proxy-provider, Select Group 和 Auto Group。该功能是为每个配置每个文件的生成这些组。 163 | 164 | 比如: 165 | 166 |
167 | tpl_basic.yaml 168 | 169 | ```yaml 170 | proxy-groups: 171 | - name: "Entry" 172 | type: select 173 | proxies: 174 | - AllAuto 175 | - AllSelect 176 | # 使用名为 "Auto" 组下的所有组 177 | - 178 | url: http://www.gstatic.com/generate_204 179 | interval: 300 180 | 181 | - name: "AllSelect" 182 | type: select 183 | use: 184 | # 使用名为 "provider" 下的 providers 185 | - 186 | url: http://www.gstatic.com/generate_204 187 | interval: 300 188 | 189 | - name: "AllAuto" 190 | type: url-test 191 | proxies: 192 | - 193 | url: http://www.gstatic.com/generate_204 194 | interval: 30 195 | 196 | # 为模板名为 "provider" 下的 providres 生成组 197 | - name: "Select" 198 | tpl_param: 199 | providers: ["provider"] 200 | type: select 201 | use: null 202 | url: http://www.gstatic.com/generate_204 203 | interval: 300 204 | 205 | - name: "Auto" 206 | tpl_param: 207 | providers: ["provider"] 208 | type: url-test 209 | use: null 210 | url: http://www.gstatic.com/generate_204 211 | interval: 300 212 | 213 | - name: "RuleMode" 214 | type: select 215 | proxies: 216 | - DIRECT 217 | - Entry 218 | 219 | - name: "RuleMode-LastMatch" 220 | type: select 221 | proxies: 222 | - Entry 223 | - DIRECT 224 | 225 | proxy-providers: 226 | # 为所有 url 生成 providers 227 | provider: 228 | tpl_param: 229 | type: http 230 | url: null 231 | path: null 232 | interval: 3600 233 | health-check: 234 | enable: true 235 | interval: 600 236 | url: http://www.gstatic.com/generate_204 237 | ``` 238 | 239 |
240 | 241 |
242 | proxy_provider_urls (如果这些 url 的内容如果没有 proxies 的内容, 会使用 SubConverter 来转换。) 243 | 244 | ```yaml 245 | https://example1.com 246 | https://example2.com 247 | ``` 248 | 249 |
250 | 251 |
252 | 生成的 tpl_basic.yaml 253 | 254 | ```yaml 255 | proxy-groups: 256 | - name: Entry 257 | type: select 258 | proxies: 259 | - AllAuto 260 | - AllSelect 261 | - Auto-provider0 262 | - Auto-provider1 263 | url: http://www.gstatic.com/generate_204 264 | interval: 300 265 | - name: AllSelect 266 | type: select 267 | use: 268 | - provider0 269 | - provider1 270 | url: http://www.gstatic.com/generate_204 271 | interval: 300 272 | - name: AllAuto 273 | type: url-test 274 | proxies: 275 | - Auto-provider0 276 | - Auto-provider1 277 | url: http://www.gstatic.com/generate_204 278 | interval: 30 279 | - name: Select-provider0 280 | type: select 281 | use: 282 | - provider0 283 | url: http://www.gstatic.com/generate_204 284 | interval: 300 285 | - name: Select-provider1 286 | type: select 287 | use: 288 | - provider1 289 | url: http://www.gstatic.com/generate_204 290 | interval: 300 291 | - name: Auto-provider0 292 | type: url-test 293 | use: 294 | - provider0 295 | url: http://www.gstatic.com/generate_204 296 | interval: 300 297 | - name: Auto-provider1 298 | type: url-test 299 | use: 300 | - provider1 301 | url: http://www.gstatic.com/generate_204 302 | interval: 300 303 | - name: RuleMode 304 | type: select 305 | proxies: 306 | - DIRECT 307 | - Entry 308 | - name: RuleMode-LastMatch 309 | type: select 310 | proxies: 311 | - Entry 312 | - DIRECT 313 | proxy-providers: 314 | provider0: 315 | type: http 316 | url: 317 | https://example1.com 318 | path: proxy-providers/tpl/provider0.yaml 319 | interval: 3600 320 | health-check: 321 | enable: true 322 | interval: 600 323 | url: http://www.gstatic.com/generate_204 324 | provider1: 325 | type: http 326 | url: 327 | https://example1.com 328 | path: proxy-providers/tpl/provider1.yaml 329 | interval: 3600 330 | health-check: 331 | enable: true 332 | interval: 600 333 | url: http://www.gstatic.com/generate_204 334 | ``` 335 | 336 |
337 | 338 | 会将合并后的文件复制到 profiles 目录, 然后更新并选择这个 profile 即可。 339 | 340 | ## 更新 ClashAuto 341 | 342 | 从 Release 下载软件, 解压软件压缩包, 然后将之前的 data 的文件 (选择自己需要的) 复制到安装目录下即可。 343 | 344 | ## Linux 345 | 346 | ### 依赖 347 | 348 | 1. 安装 python, python-pip 349 | 2. pip install ruamel.yaml requests 350 | 351 | ### 安装 352 | 353 | 比如: 354 | 355 | 1. 安装 Clash premium。并确保有 clash server unit (`systemctl cat clash@` 可以查看)。比如 ArchLinux: `yay -S clash-premium-bin` 356 | 357 | 安装之后, `/usr/lib/systemd/system/clash@.server` 内容如下: 358 | 359 | ``` 360 | [Unit] 361 | Description=A rule based proxy in Go for %i. 362 | After=network.target 363 | 364 | [Service] 365 | Type=exec 366 | User=%i 367 | Restart=on-abort 368 | ExecStart=/usr/bin/clash 369 | 370 | [Install] 371 | WantedBy=multi-user.target 372 | ``` 373 | 374 | 4. 运行 `clashauto`, 选择 config_clash_server。 375 | 376 | 修改: 377 | 378 | ``` 379 | [Service] 380 | # 删除原先的 ExecStart 381 | ExecStart= 382 | # 修改 `-d, -f` 参数。 383 | ExecStart=/usr/bin/clash -d /opt/clash-auto/clash_config -f /opt/clash-auto/final_clash_config.yaml 384 | ``` 385 | 386 | 或者, 创建文件 `/etc/systemd/system/clash@root.service.d/override.conf`: 387 | 388 | ``` 389 | [Service] 390 | ExecStart= 391 | ExecStart=/usr/bin/clash -d /opt/clash-auto/clash_config -f /opt/clash-auto/final_clash_config.yaml 392 | ``` 393 | 394 | ### 使用 395 | 396 | 和 Windows 平台差不多。 397 | 398 | ## Screenshots 399 | 400 | ![](./screenshots/clashauto.png) 401 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Clash Auto 2 | 3 | ## Supported Platforms 4 | 5 | - Windows 6 | 7 | ## Dependencies 8 | 9 | 1. Install Python 10 | 2. Run `pip install ruamel.yaml requests` to install the required Python packages. 11 | 12 | ## Installation 13 | 14 | 1. Download the release package (no installation required) from the release page. 15 | 2. Double-click `clashauto.bat` to run the program. 16 | 3. Select `install` to install a startup clash service for Windows. 17 | 18 | ## Usage 19 | 20 | ### The Purpose of Each File in the Software 21 | 22 | - `clashauto.bat`: Used to manage the clash service. 23 | - `config`: The configuration directory for clash. 24 | - `profiles`: Used to store profile files. 25 | - `basic_clash_config.yaml`: Used to configure basic clash settings. Remember to restart `clashauto.bat` after making changes to this file. 26 | - `final_clash_config.yaml`: The configuration file used to start clash after merging the `basic_clash_config` and profile files. 27 | 28 | ### Options in `clashauto.bat` 29 | 30 | After double-clicking `clashauto.bat`, you will see the following options: 31 | 32 | - `update_final_config`: Update the resources required by `final_clash_config.yaml`. 33 | - `update_profile_config`: 34 | 35 | Update the resources required by profile files. 36 | 37 | You can place a file with the suffix `_url` containing a link to a profile. It will save the content of the link to a file with `_url` replaced by `.yaml` and update the resources required by that profile. If the content of the URL is not a Clash configuration file, it can be converted into a Clash configuration file through some subscription conversion websites, such as https://acl4ssr-sub.github.io/. 38 | 39 | - `select_profile`: Select a profile and merge it with `basic_clash_config.yaml` to generate `final_clash_config.yaml`. Then restart the clash service to make the new configuration take effect. 40 | - `restart/stop/config/install/uninstall`: Used to manage the clash service. 41 | 42 | *Clash Auto uses its own proxy to update the dependencies of the clash configuration file and update the clash configuration file itself.* 43 | 44 | ### Clash Client 45 | 46 | Access http://127.0.0.1:9090/ui in your browser to use the client. 47 | 48 | ## Q&A 49 | 50 | Q: What should I do if the clash client can connect to the server, but cannot access blocked content? 51 | 52 | A: You may need to configure Windows Firewall to allow clash.exe to access the internet. 53 | 54 | ## Screenshots 55 | 56 | ![](./screenshots/clashauto.png) 57 | -------------------------------------------------------------------------------- /clashauto: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding: UTF-8 _*_ 3 | 4 | import os, sys, shutil, configparser 5 | from enum import Enum 6 | import ruamel.yaml 7 | import requests 8 | from clashautoutil import ClashUtil 9 | 10 | # ## Global Vars 11 | SCRIPT_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) 12 | DATA_DIR = os.path.join(SCRIPT_PATH, "data") 13 | CLASH_CFG_DIR = os.path.join(SCRIPT_PATH, "clash_config") 14 | PROFILE_DIR = os.path.join(DATA_DIR, "profiles") 15 | TPL_DIR = os.path.join(DATA_DIR, "tpl") 16 | 17 | BASIC_CLASH_CONFIG_PATH = os.path.join(DATA_DIR, "basic_clash_config.yaml") 18 | FINAL_CLASH_CONFIG_PATH = os.path.join(SCRIPT_PATH, "final_clash_config.yaml") 19 | CLASHAUTO_CONFIG_PATH = os.path.join(DATA_DIR, "config.ini") 20 | PROXY_PROVIDERS_URL_PATH = os.path.join(TPL_DIR, "proxy_provider_urls") 21 | 22 | TIMEOUT = 5 23 | PATH_OF_UPDATE_CFG_RES = os.path.join(SCRIPT_PATH, "update_clashcfg_res.py") 24 | 25 | def select(options): 26 | print("Please select an option:") 27 | for i, item in enumerate(options): 28 | print(i, item) 29 | 30 | try: 31 | choice = int(input("Enter your choice (num): ")) 32 | except ValueError: 33 | choice = len(options) 34 | if choice < 0: 35 | choice = len(options) 36 | 37 | return choice 38 | 39 | def get_file_names(dir_path): 40 | file_names = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] 41 | file_names.sort() 42 | return file_names 43 | 44 | class ServerCmd(Enum): 45 | RESTART = 1 46 | STOP = 2 47 | CONFIG = 3 48 | INSTALL = 4 49 | UNINSTALL = 5 50 | TEST = 6 51 | 52 | def clash_server_ctl(server_cmd): 53 | os.chdir(SCRIPT_PATH) 54 | 55 | cmd = "" 56 | if server_cmd == ServerCmd.RESTART: 57 | cmd = f"sudo systemctl restart clash@root" 58 | elif server_cmd == ServerCmd.STOP: 59 | cmd = f"sudo systemctl stop clash@root" 60 | elif server_cmd == ServerCmd.CONFIG: 61 | cmd = f"sudo systemctl edit clash@root" 62 | elif server_cmd == ServerCmd.TEST: 63 | cmd = f'clash -d "{CLASH_CFG_DIR}" -f "{FINAL_CLASH_CONFIG_PATH}" -t' 64 | print(cmd) 65 | 66 | os.system(cmd) 67 | 68 | def main(): 69 | #if os.geteuid() != 0: 70 | # os.execvp("sudo", ["sudo", * sys.argv]) 71 | # return 0 72 | 73 | os.chdir(SCRIPT_PATH) 74 | 75 | config = configparser.ConfigParser() 76 | config.read(CLASHAUTO_CONFIG_PATH) 77 | sc_host = config.get("main", "sc_host") 78 | 79 | with open(BASIC_CLASH_CONFIG_PATH, "r", encoding="utf-8") as f: 80 | basic_yaml_data = ruamel.yaml.safe_load(f) 81 | proxy = ClashUtil.get_proxy(basic_yaml_data) 82 | 83 | session = requests.Session() 84 | session.headers.update({"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"}) 85 | if proxy: 86 | protocol, address = proxy.split("://") 87 | session.proxies = { 88 | protocol: address 89 | } 90 | session.timeout = TIMEOUT 91 | 92 | clash_util = ClashUtil(session, CLASH_CFG_DIR, PROFILE_DIR, sc_host); 93 | 94 | options = ["update_final_config", "update_profile", "select_profile", 95 | "restart_clash_service", "stop_clash_service", \ 96 | "test_config", "create_yaml", \ 97 | "tun_mode", \ 98 | "config_clash_service", "restart_clash_auto", "exit"] 99 | while True: 100 | print() 101 | choice = select(options) 102 | if choice >= len(options): 103 | print("Invalid selection, please try again") 104 | continue 105 | choiced_option = options[choice] 106 | if choiced_option == "update_final_config": 107 | updated_res, no_update_res = clash_util.update_dep_net_res(FINAL_CLASH_CONFIG_PATH) 108 | print("updated_res:") 109 | [print(f'- {s}, {i}') for s, r in updated_res.items() for i in r] 110 | print("no_update_res:") 111 | [print(f'- {s}, {i}') for s, r in no_update_res.items() for i in r] 112 | 113 | clash_server_ctl(ServerCmd.RESTART) 114 | elif choiced_option == "update_profile": 115 | file_names = get_file_names(PROFILE_DIR) 116 | profiles = [f for f in file_names if not f.endswith("~")] 117 | profile_index = select(profiles) 118 | if profile_index >= len(profiles): 119 | print("backward") 120 | continue 121 | choiced_profile_name = profiles[profile_index] 122 | if choiced_profile_name.endswith("_url"): 123 | profile_name = choiced_profile_name[:-4] + ".yaml" 124 | profile_path = os.path.join(PROFILE_DIR, profile_name) 125 | 126 | urls = ClashUtil.extra_urls(os.path.join(PROFILE_DIR, choiced_profile_name)) 127 | profile_url = "" 128 | if urls: 129 | profile_url = "|".join(urls) 130 | out_url, out_content = clash_util.fetch_sub_url(profile_url) 131 | if out_content: 132 | with open(profile_path, "wb") as f: 133 | f.write(out_content) 134 | print(f'Updated profile: "{profile_path}", "{out_url}"') 135 | else: 136 | print(f'Failed to update profile: out_url="{out_url}"') 137 | print("backward") 138 | continue 139 | else: 140 | profile_name = choiced_profile_name 141 | profile_path = os.path.join(PROFILE_DIR, profile_name) 142 | 143 | sections = ["proxy-providers", "rule-providers"] 144 | updated_res, no_update_res = clash_util.update_dep_net_res(profile_path, sections) 145 | print("updated_res:") 146 | [print(f'- {s}, {i}') for s, r in updated_res.items() for i in r] 147 | print("no_update_res:") 148 | [print(f'- {s}, {i}') for s, r in no_update_res.items() for i in r] 149 | 150 | elif choiced_option == "select_profile": 151 | file_names = get_file_names(PROFILE_DIR) 152 | profiles = [f for f in file_names if not f.endswith("~") and not f.endswith("_url")] 153 | profile_index = select(profiles) 154 | if profile_index >= len(profiles): 155 | print("backward") 156 | continue 157 | choiced_profile_name = profiles[profile_index] 158 | profile_name = choiced_profile_name 159 | 160 | profile_path = os.path.join(PROFILE_DIR, profile_name) 161 | with open(profile_path, "r", encoding="utf-8") as f: 162 | profile_yaml_data = ruamel.yaml.safe_load(f) 163 | merged_yaml_data = ClashUtil.merge_profile(basic_yaml_data, profile_yaml_data) 164 | with open(FINAL_CLASH_CONFIG_PATH, "w", encoding="utf-8", newline="") as f: 165 | ruamel.yaml.round_trip_dump(merged_yaml_data, f) 166 | print(f'Merged "{BASIC_CLASH_CONFIG_PATH}" and "{profile_path}" into "{FINAL_CLASH_CONFIG_PATH}"') 167 | 168 | tun_mode = config.getboolean("main", "tun_mode", fallback=None) 169 | if tun_mode: 170 | ClashUtil.tun_ctl(enable=True, config_path=FINAL_CLASH_CONFIG_PATH) 171 | else: 172 | ClashUtil.tun_ctl(enable=False, config_path=FINAL_CLASH_CONFIG_PATH) 173 | clash_server_ctl(ServerCmd.RESTART) 174 | 175 | elif choiced_option == "restart_clash_service": 176 | clash_server_ctl(ServerCmd.RESTART) 177 | elif choiced_option == "stop_clash_service": 178 | clash_server_ctl(ServerCmd.STOP) 179 | elif choiced_option == "config_clash_service": 180 | clash_server_ctl(ServerCmd.CONFIG) 181 | clash_server_ctl(ServerCmd.RESTART) 182 | elif choiced_option == "test_config": 183 | clash_server_ctl(ServerCmd.TEST) 184 | elif choiced_option == "create_yaml": 185 | file_names = get_file_names(os.path.join(DATA_DIR, "tpl")) 186 | file_names = [i for i in file_names if os.path.splitext(i)[1] == ".yaml"] 187 | index = select(file_names) 188 | if index >= len(file_names): 189 | print("backward") 190 | continue 191 | tpl_config_path = os.path.join(TPL_DIR, file_names[index]) 192 | tpl_out_config_path = os.path.join(PROFILE_DIR, file_names[index]) 193 | url_path = PROXY_PROVIDERS_URL_PATH 194 | urls = ClashUtil.extra_urls(url_path) 195 | proxy_urls, failed_urls = clash_util.create_yaml_base_on_tpl(urls, tpl_config_path, tpl_out_config_path) 196 | print("used providers:") 197 | [print(f"- {i}") for i in proxy_urls] 198 | print("failed providers:") 199 | [print(f"- {i}") for i in failed_urls] 200 | print(f'created "{tpl_out_config_path}"') 201 | elif choiced_option == "tun_mode": 202 | opts = ["enable", "disable"] 203 | idx = select(opts) 204 | if idx >= len(opts): 205 | print("backward") 206 | continue 207 | 208 | if opts[idx] == "enable": 209 | enable = True 210 | else: 211 | enable = False 212 | 213 | config.set("main", "tun_mode", "True" if enable else "False") 214 | with open(CLASHAUTO_CONFIG_PATH, "w", encoding="utf-8", newline="") as f: 215 | config.write(f) 216 | 217 | ClashUtil.tun_ctl(enable, FINAL_CLASH_CONFIG_PATH) 218 | print(f'Switched tun mode: {enable}') 219 | 220 | clash_server_ctl(ServerCmd.RESTART) 221 | elif choiced_option == "restart_clash_auto": 222 | exec = sys.executable 223 | os.execl(exec, exec, * sys.argv) 224 | elif choiced_option == "exit": 225 | break; 226 | else: 227 | sys.stderr.write("Not match the option") 228 | 229 | if __name__ == "__main__": 230 | main() 231 | -------------------------------------------------------------------------------- /clashauto-win.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding: UTF-8 _*_ 3 | 4 | import os, sys, subprocess, shutil, configparser 5 | sys.path.append(".") 6 | from enum import Enum 7 | import ruamel.yaml 8 | import requests 9 | from clashautoutil import ClashUtil 10 | 11 | # ## Global Vars 12 | SCRIPT_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) 13 | DATA_DIR = os.path.join(SCRIPT_PATH, "data") 14 | CLASH_CFG_DIR = os.path.join(SCRIPT_PATH, "clash_config") 15 | PROFILE_DIR = os.path.join(DATA_DIR, "profiles") 16 | TPL_DIR = os.path.join(DATA_DIR, "tpl") 17 | 18 | BASIC_CLASH_CONFIG_PATH = os.path.join(DATA_DIR, "basic_clash_config.yaml") 19 | FINAL_CLASH_CONFIG_PATH = os.path.join(SCRIPT_PATH, "final_clash_config.yaml") 20 | CLASHAUTO_CONFIG_PATH = os.path.join(DATA_DIR, "config.ini") 21 | PROXY_PROVIDERS_URL_PATH = os.path.join(TPL_DIR, "proxy_provider_urls") 22 | CLASH_CORE = os.path.join(SCRIPT_PATH, "clash.exe") 23 | 24 | TIMEOUT = 5 25 | PATH_OF_UPDATE_CFG_RES = os.path.join(SCRIPT_PATH, "update_clashcfg_res.py") 26 | 27 | def select(options): 28 | print("Please select an option:") 29 | for i, item in enumerate(options): 30 | print(i, item) 31 | 32 | try: 33 | choice = int(input("Enter your choice (num): ")) 34 | except ValueError: 35 | choice = len(options) 36 | if choice < 0: 37 | choice = len(options) 38 | 39 | return choice 40 | 41 | def get_file_names(dir_path): 42 | file_names = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] 43 | file_names.sort() 44 | return file_names 45 | 46 | class ServerCmd(Enum): 47 | RESTART = 1 48 | STOP = 2 49 | CONFIG = 3 50 | INSTALL = 4 51 | UNINSTALL = 5 52 | TEST = 6 53 | 54 | def clash_server_ctl(server_cmd): 55 | os.chdir(SCRIPT_PATH) 56 | 57 | cmd = "" 58 | if server_cmd == ServerCmd.RESTART: 59 | cmd = f"nssm.exe restart clash" 60 | elif server_cmd == ServerCmd.STOP: 61 | cmd = f"nssm.exe stop clash" 62 | elif server_cmd == ServerCmd.CONFIG: 63 | cmd = f"nssm.exe edit clash" 64 | elif server_cmd == ServerCmd.INSTALL: 65 | cmd = f'nssm.exe install clash "{CLASH_CORE}" -d "{CLASH_CFG_DIR}" -f "{FINAL_CLASH_CONFIG_PATH}"' 66 | elif server_cmd == ServerCmd.UNINSTALL: 67 | cmd = f"nssm.exe remove clash confirm" 68 | elif server_cmd == ServerCmd.TEST: 69 | cmd = f'{CLASH_CORE} -d "{CLASH_CFG_DIR}" -f "{FINAL_CLASH_CONFIG_PATH}" -t' 70 | print(cmd) 71 | 72 | os.system(cmd) 73 | 74 | def win_system_proxy_ctl(enable, proxy=None): 75 | try: 76 | if enable: 77 | # Use reg command to enable the proxy with ProxyEnable = 1 78 | subprocess.run(["reg", "add", R"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "/v", "ProxyEnable", "/t", "REG_DWORD", "/d", "1", "/f"]) 79 | subprocess.run(["reg", "add", R"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "/v", "ProxyServer", "/t", "REG_SZ", "/d", proxy, "/f"]) 80 | 81 | print("System proxy has been enabled.") 82 | else: 83 | # Use reg command to disable the proxy with ProxyEnable = 0 84 | subprocess.run(["reg", "add", R"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "/v", "ProxyEnable", "/t", "REG_DWORD", "/d", "0", "/f"]) 85 | #subprocess.run(["reg", "delete", R"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "/v", "ProxyServer", "/f"]) 86 | 87 | print("System proxy has been disabled.") 88 | except Exception as e: 89 | print("Error occurred while configuring the system proxy:", str(e)) 90 | 91 | def main(): 92 | os.chdir(SCRIPT_PATH) 93 | 94 | config = configparser.ConfigParser() 95 | config.read(CLASHAUTO_CONFIG_PATH) 96 | sc_host = config.get("main", "sc_host") 97 | 98 | with open(BASIC_CLASH_CONFIG_PATH, "r", encoding="utf-8") as f: 99 | basic_yaml_data = ruamel.yaml.safe_load(f) 100 | proxy = ClashUtil.get_proxy(basic_yaml_data) 101 | 102 | session = requests.Session() 103 | session.headers.update({"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"}) 104 | if proxy: 105 | protocol, address = proxy.split("://") 106 | session.proxies = { 107 | protocol: address 108 | } 109 | session.timeout = TIMEOUT 110 | 111 | clash_util = ClashUtil(session, CLASH_CFG_DIR, PROFILE_DIR, sc_host); 112 | 113 | options = ["update_final_config", "update_profile", "select_profile", \ 114 | "restart_clash_service", "stop_clash_service", \ 115 | "test_config", "create_yaml", \ 116 | "tun_mode", "system_proxy", "uwp_loopback", \ 117 | "config_clash_service", "install_clash_service", "uninstall_clash_service", \ 118 | "restart_clash_auto", "exit"] 119 | while True: 120 | print() 121 | choice = select(options) 122 | if choice >= len(options): 123 | print("Invalid selection, please try again") 124 | continue 125 | choiced_option = options[choice] 126 | if choiced_option == "update_final_config": 127 | updated_res, no_update_res = clash_util.update_dep_net_res(FINAL_CLASH_CONFIG_PATH) 128 | print("updated_res:") 129 | [print(f'- {s}, {i}') for s, r in updated_res.items() for i in r] 130 | print("no_update_res:") 131 | [print(f'- {s}, {i}') for s, r in no_update_res.items() for i in r] 132 | 133 | clash_server_ctl(ServerCmd.RESTART) 134 | elif choiced_option == "update_profile": 135 | file_names = get_file_names(PROFILE_DIR) 136 | profiles = [f for f in file_names if not f.endswith("~")] 137 | profile_index = select(profiles) 138 | if profile_index >= len(profiles): 139 | print("backward") 140 | continue 141 | choiced_profile_name = profiles[profile_index] 142 | if choiced_profile_name.endswith("_url"): 143 | profile_name = choiced_profile_name[:-4] + ".yaml" 144 | profile_path = os.path.join(PROFILE_DIR, profile_name) 145 | 146 | urls = ClashUtil.extra_urls(os.path.join(PROFILE_DIR, choiced_profile_name)) 147 | profile_url = "" 148 | if urls: 149 | profile_url = "|".join(urls) 150 | out_url, out_content = clash_util.fetch_sub_url(profile_url) 151 | if out_content: 152 | with open(profile_path, "wb") as f: 153 | f.write(out_content) 154 | print(f'Updated profile: "{profile_path}", "{out_url}"') 155 | else: 156 | print(f'Failed to update profile: out_url="{out_url}"') 157 | print("backward") 158 | continue 159 | else: 160 | profile_name = choiced_profile_name 161 | profile_path = os.path.join(PROFILE_DIR, profile_name) 162 | 163 | sections = ["proxy-providers", "rule-providers"] 164 | updated_res, no_update_res = clash_util.update_dep_net_res(profile_path, sections) 165 | print("updated_res:") 166 | [print(f'- {s}, {i}') for s, r in updated_res.items() for i in r] 167 | print("no_update_res:") 168 | [print(f'- {s}, {i}') for s, r in no_update_res.items() for i in r] 169 | 170 | elif choiced_option == "select_profile": 171 | file_names = get_file_names(PROFILE_DIR) 172 | profiles = [f for f in file_names if not f.endswith("~") and not f.endswith("_url")] 173 | profile_index = select(profiles) 174 | if profile_index >= len(profiles): 175 | print("backward") 176 | continue 177 | choiced_profile_name = profiles[profile_index] 178 | profile_name = choiced_profile_name 179 | 180 | profile_path = os.path.join(PROFILE_DIR, profile_name) 181 | with open(profile_path, "r", encoding="utf-8") as f: 182 | profile_yaml_data = ruamel.yaml.safe_load(f) 183 | merged_yaml_data = ClashUtil.merge_profile(basic_yaml_data, profile_yaml_data) 184 | with open(FINAL_CLASH_CONFIG_PATH, "w", encoding="utf-8", newline="") as f: 185 | ruamel.yaml.round_trip_dump(merged_yaml_data, f) 186 | print(f'Merged "{BASIC_CLASH_CONFIG_PATH}" and "{profile_path}" into "{FINAL_CLASH_CONFIG_PATH}"') 187 | 188 | tun_mode = config.getboolean("main", "tun_mode", fallback=None) 189 | if tun_mode: 190 | ClashUtil.tun_ctl(enable=True, config_path=FINAL_CLASH_CONFIG_PATH) 191 | else: 192 | ClashUtil.tun_ctl(enable=False, config_path=FINAL_CLASH_CONFIG_PATH) 193 | clash_server_ctl(ServerCmd.RESTART) 194 | 195 | elif choiced_option == "restart_clash_service": 196 | clash_server_ctl(ServerCmd.RESTART) 197 | elif choiced_option == "stop_clash_service": 198 | clash_server_ctl(ServerCmd.STOP) 199 | elif choiced_option == "config_clash_service": 200 | clash_server_ctl(ServerCmd.CONFIG) 201 | clash_server_ctl(ServerCmd.RESTART) 202 | elif choiced_option == "test_config": 203 | clash_server_ctl(ServerCmd.TEST) 204 | elif choiced_option == "create_yaml": 205 | file_names = get_file_names(os.path.join(DATA_DIR, "tpl")) 206 | file_names = [i for i in file_names if os.path.splitext(i)[1] == ".yaml"] 207 | index = select(file_names) 208 | if index >= len(file_names): 209 | print("backward") 210 | continue 211 | tpl_config_path = os.path.join(TPL_DIR, file_names[index]) 212 | tpl_out_config_path = os.path.join(PROFILE_DIR, file_names[index]) 213 | url_path = PROXY_PROVIDERS_URL_PATH 214 | urls = ClashUtil.extra_urls(url_path) 215 | proxy_urls, failed_urls = clash_util.create_yaml_base_on_tpl(urls, tpl_config_path, tpl_out_config_path) 216 | print("used providers:") 217 | [print(f"- {i}") for i in proxy_urls] 218 | print("failed providers:") 219 | [print(f"- {i}") for i in failed_urls] 220 | print(f'created "{tpl_out_config_path}"') 221 | elif choiced_option == "tun_mode": 222 | opts = ["enable", "disable"] 223 | idx = select(opts) 224 | if idx >= len(opts): 225 | print("backward") 226 | continue 227 | 228 | if opts[idx] == "enable": 229 | enable = True 230 | else: 231 | enable = False 232 | 233 | config.set("main", "tun_mode", "True" if enable else "False") 234 | with open(CLASHAUTO_CONFIG_PATH, "w", encoding="utf-8", newline="") as f: 235 | config.write(f) 236 | 237 | ClashUtil.tun_ctl(enable, FINAL_CLASH_CONFIG_PATH) 238 | print(f'Switched tun mode: {enable}') 239 | 240 | clash_server_ctl(ServerCmd.RESTART) 241 | elif choiced_option == "system_proxy": 242 | opts = ["enable", "disable"] 243 | idx = select(opts) 244 | if idx >= len(opts): 245 | print("backward") 246 | continue 247 | 248 | if opts[idx] == "enable": 249 | enable = True 250 | else: 251 | enable = False 252 | win_system_proxy_ctl(enable=enable, proxy=proxy) 253 | elif choiced_option == "uwp_loopback": 254 | cmd = f'EnableLoopback.exe' 255 | os.system(cmd) 256 | elif choiced_option == "install_clash_service": 257 | clash_server_ctl(ServerCmd.INSTALL) 258 | clash_server_ctl(ServerCmd.RESTART) 259 | elif choiced_option == "uninstall_clash_service": 260 | clash_server_ctl(ServerCmd.STOP) 261 | clash_server_ctl(ServerCmd.UNINSTALL) 262 | win_system_proxy_ctl(enable=False) 263 | elif choiced_option == "restart_clash_auto": 264 | #exec = sys.executable 265 | #os.execl(exec, exec, * sys.argv) 266 | os.execl("clashauto.bat", "clashauto.bat") 267 | elif choiced_option == "exit": 268 | break; 269 | else: 270 | sys.stderr.write("Not match the option") 271 | 272 | if __name__ == "__main__": 273 | main() 274 | -------------------------------------------------------------------------------- /clashauto.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: BatchGotAdmin (Run as Admin code starts) 3 | REM --> Check for permissions 4 | >nul 2>&1 "%SYSTEMROOT%\system32\icacls.exe" "%SYSTEMROOT%\system32\config\system" 5 | REM --> If error flag set, we do not have admin. 6 | if '%errorlevel%' NEQ '0' ( 7 | echo Requesting administrative privileges... 8 | goto UACPrompt 9 | ) else ( goto gotAdmin ) 10 | 11 | :UACPrompt 12 | echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" 13 | set params = %*:"="" 14 | echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" 15 | 16 | "%temp%\getadmin.vbs" 17 | del "%temp%\getadmin.vbs" 18 | exit /B 19 | 20 | :gotAdmin 21 | pushd "%CD%" 22 | CD /D "%~dp0" 23 | :: BatchGotAdmin (Run as Admin code ends) 24 | 25 | :: Your code goes below this line 26 | 27 | .\python\python.exe %~dp0clashauto-win.py 28 | pause 29 | -------------------------------------------------------------------------------- /clashautoutil.py: -------------------------------------------------------------------------------- 1 | import sys, os, copy, urllib.parse 2 | import ruamel.yaml, requests 3 | 4 | class ClashUtil(): 5 | def __init__(self, session, cfg_dir, profile_dir, sc_host=None): 6 | self.__session = session 7 | self.__cfg_dir = cfg_dir 8 | self.__profile_dir = profile_dir 9 | self.__sc_host = sc_host 10 | 11 | def update_dep_net_res(self, profile_path, sections=["proxy-providers"], list=True): 12 | oldcwd = os.getcwd() 13 | os.chdir(self.__cfg_dir) 14 | 15 | net_res = self.get_dep_net_res(profile_path, sections) 16 | updated_res = {} 17 | no_update_res = {} 18 | for s, r in net_res.items(): 19 | updated_res[s] = [] 20 | no_update_res[s] = [] 21 | for i in r: 22 | dirpath = os.path.dirname(i["path"]) 23 | if not os.path.exists(dirpath): 24 | os.makedirs(dirpath) 25 | 26 | if s == "proxy-providers": 27 | url, content = self.fetch_sub_url(i["url"], list) 28 | else: 29 | with self.__session.get(i["url"]) as response: 30 | content = response.content 31 | 32 | if content: 33 | with open(i["path"], "wb") as f: 34 | f.write(content) 35 | updated_res[s].append(i) 36 | else: 37 | no_update_res[s].append(i) 38 | 39 | os.chdir(oldcwd) 40 | return updated_res, no_update_res; 41 | 42 | def get_dep_net_res(self, profile_path, sections): 43 | # ## Load cfg 44 | with open(profile_path, "r", encoding="utf-8") as f: 45 | cfg_data = ruamel.yaml.safe_load(f) 46 | 47 | net_res = {} 48 | for s in sections: 49 | # ## extras data from section 50 | s_data = cfg_data.get(s) 51 | if not s_data: 52 | continue 53 | 54 | # ## Put the url and path of items with type == "http" into net_res. 55 | net_res[s] = [] 56 | for i in s_data.values(): 57 | if i["type"] == "http": 58 | net_res[s].append({"url": i["url"], "path": i["path"]}) 59 | 60 | return net_res 61 | 62 | def fetch_sub_url(self, url, list=False, rename=None): 63 | out_url, out_conent = self.convert_to_clash_yaml_url(url, list=list, rename=rename) 64 | return out_url, out_conent 65 | 66 | def convert_to_clash_yaml_url(self, url, *, must_use_sc_host=False, list=False, rename=None): 67 | out_url = None 68 | out_content = None 69 | try: 70 | if not must_use_sc_host: 71 | # If url content is clash yaml 72 | if url.startswith("http"): 73 | with self.__session.get(url) as response: 74 | yaml_data = ruamel.yaml.safe_load(response.content.decode("utf-8")) 75 | if self.is_clash_yaml(yaml_data): 76 | out_url = url 77 | out_content = response.content 78 | return out_url, out_content 79 | 80 | # use subconverter 81 | sc_url = self.create_subconverter_url(url, self.__sc_host, list, rename) 82 | with self.__session.get(sc_url) as response: 83 | yaml_data = ruamel.yaml.safe_load(response.content.decode("utf-8")) 84 | if self.is_clash_yaml(yaml_data): 85 | out_url = sc_url 86 | out_content = response.content 87 | else: 88 | out_url = sc_url 89 | out_content = None 90 | except Exception as e: 91 | print("exceptions:", e) 92 | return None, None 93 | 94 | return out_url, out_content 95 | 96 | @staticmethod 97 | def create_subconverter_url(url, host, list=False, rename=None): 98 | encoded_url = urllib.parse.quote(url, safe="") 99 | subconverter_url = r"{}".format(f"https://{host}/sub?target=clash&url={encoded_url}") 100 | if list: 101 | subconverter_url += r"&list=true" 102 | if rename: 103 | encode_rename = urllib.parse.quote(rename, safe="") 104 | subconverter_url += r"&rename={}".format(encode_rename) 105 | return subconverter_url 106 | 107 | @staticmethod 108 | def is_clash_yaml(yaml_data): 109 | result = False 110 | try: 111 | if yaml_data.get("proxies") or yaml_data.get("proxy-groups"): 112 | result = True 113 | except Exception as e: 114 | return False 115 | 116 | return result 117 | 118 | @staticmethod 119 | def merge_profile(basic_yaml_data, profile_yaml_data): 120 | merged_data = {} 121 | for key, value in basic_yaml_data.items(): 122 | if key not in merged_data: 123 | merged_data[key] = value 124 | for key, value in profile_yaml_data.items(): 125 | if key not in ["proxy-groups", "proxy-providers", "proxies", "rules", "rule-providers"]: 126 | continue 127 | if key not in merged_data: 128 | merged_data[key] = value 129 | return merged_data 130 | 131 | def create_yaml_base_on_tpl(self, urls, tpl_yaml_path, out_yaml_path): 132 | proxy_urls = [] 133 | failed_urls = [] 134 | if self.__sc_host: 135 | for i, url in enumerate(urls): 136 | out_url, out_content = self.convert_to_clash_yaml_url(url, must_use_sc_host=True, list=True) 137 | if out_content: 138 | proxy_urls.append(out_url) 139 | else: 140 | #failed_urls.append(url) 141 | failed_urls.append(out_url) 142 | else: 143 | proxy_urls = urls 144 | 145 | with open(tpl_yaml_path, "r", encoding="utf-8") as f: 146 | tpl_yaml_data = ruamel.yaml.safe_load(f) 147 | 148 | out_yaml_data = copy.deepcopy(tpl_yaml_data) 149 | 150 | # ## proxy-providers 151 | pp_section_name = "proxy-providers" 152 | provider_data, provider_names = self.__create_providers_base_on_tpl(proxy_urls, tpl_yaml_data[pp_section_name]) 153 | out_yaml_data[pp_section_name] = provider_data 154 | 155 | # ## proxy-groups 156 | pg_section_name = "proxy-groups" 157 | new_group_data = self.__create_proxy_groups_base_on_tpl(provider_names, tpl_yaml_data[pg_section_name]) 158 | out_yaml_data[pg_section_name] = new_group_data 159 | 160 | os.makedirs(os.path.dirname(out_yaml_path), exist_ok=True) 161 | with open(out_yaml_path, "w", encoding="utf-8", newline="") as f: 162 | yaml = ruamel.yaml.YAML() 163 | #yaml.indent(mapping=2, sequence=4, offset=2) 164 | #yaml.explicit_start = True 165 | #yaml.default_flow_style = False 166 | yaml.dump(out_yaml_data, f) 167 | 168 | return proxy_urls, failed_urls 169 | 170 | @staticmethod 171 | def get_proxy(yaml_data): 172 | port = yaml_data.get("mixed-port") 173 | if port: 174 | proxy = f"https://127.0.0.1:{port}" 175 | port = yaml_data.get("port") 176 | if port: 177 | proxy = f"https://127.0.0.1:{port}" 178 | port = yaml_data.get("socks-port") 179 | if port: 180 | proxy = f"socks5://127.0.0.1:{port}" 181 | 182 | return proxy if proxy else "https://127.0.0.1:7890" 183 | 184 | @staticmethod 185 | def extra_urls(file_path): 186 | urls = [] 187 | with open(file_path) as f: 188 | lines = f.readlines() 189 | for i in lines: 190 | i = i.strip() 191 | if i and not i.startswith("#"): 192 | urls.append(r"{}".format(i)) 193 | return urls 194 | 195 | @staticmethod 196 | def __create_providers_base_on_tpl(proxy_urls, tpl_proxy_providers): 197 | provider_names = {} 198 | new_provider_data = {} 199 | for name, value in tpl_proxy_providers.items(): 200 | if "tpl_param" not in value: 201 | new_provider_data[name] = value 202 | continue 203 | 204 | provider_names[name] = [] 205 | for i, u in enumerate(proxy_urls): 206 | n = f"{name}{i}" 207 | provider_names[name].append(n) 208 | new_provider_data[n] = copy.deepcopy(value) 209 | rename = r"{}".format(f"(^.*$)@{n}\|$1") 210 | encode_rename = urllib.parse.quote(rename, safe="") 211 | new_provider_data[n]["url"] = f'{u}&rename={encode_rename}' 212 | new_provider_data[n]["path"] = f"proxy-providers/tpl/{n}.yaml" 213 | del new_provider_data[n]["tpl_param"] 214 | 215 | return new_provider_data, provider_names 216 | 217 | @staticmethod 218 | def __create_proxy_groups_base_on_tpl(provider_names, tpl_proxy_groups): 219 | new_group_names = {} 220 | new_group_data = [] 221 | for i, group in enumerate(tpl_proxy_groups): 222 | if "tpl_param" not in group: 223 | new_group_data.append(group) 224 | continue 225 | 226 | new_group_names[group["name"]] = [] 227 | for pn in group["tpl_param"]["providers"]: 228 | for n in provider_names[pn]: 229 | name = f'{group["name"]}-{n}' 230 | new_group_names[group["name"]].append(name) 231 | new_group = copy.deepcopy(group) 232 | new_group["name"] = name 233 | new_group["use"] = [n] 234 | del new_group["tpl_param"] 235 | 236 | new_group_data.append(new_group) 237 | 238 | ClashUtil.__replace_special_key_in_proxy_groups(provider_names, new_group_names, new_group_data) 239 | 240 | return new_group_data 241 | 242 | @staticmethod 243 | def __replace_special_key_in_proxy_groups(provider_names, new_group_names, group_data): 244 | for group in group_data: 245 | def replace_special_key(replaced_key, replace_data): 246 | new_value_data = [] 247 | if replaced_key in group: 248 | for i in group[replaced_key]: 249 | if i.startswith("<") and i.endswith(">") and i[1:-1] in replace_data: 250 | new_value_data.extend(replace_data[i[1:-1]]) 251 | else: 252 | new_value_data.append(i) 253 | group[replaced_key] = new_value_data 254 | replace_special_key("use", provider_names) 255 | replace_special_key("proxies", new_group_names) 256 | 257 | @staticmethod 258 | def tun_ctl(enable, config_path): 259 | with open(config_path, "r", encoding="utf-8") as f: 260 | yaml_data = ruamel.yaml.safe_load(f) 261 | 262 | try: 263 | yaml_data.get("tun").get("enable") 264 | does_have_tun = True 265 | except Exception as e: 266 | does_have_tun = False 267 | try: 268 | yaml_data.get("dns").get("enable") 269 | does_have_dns = True 270 | except Exception as e: 271 | does_have_dns = False 272 | 273 | if does_have_tun: 274 | yaml_data["tun"]["enable"] = enable 275 | if does_have_dns: 276 | yaml_data["dns"]["enable"] = enable 277 | 278 | with open(config_path, "w", encoding="utf-8", newline="") as f: 279 | yaml = ruamel.yaml.YAML() 280 | yaml.dump(yaml_data, f) 281 | -------------------------------------------------------------------------------- /data/basic_clash_config.yaml: -------------------------------------------------------------------------------- 1 | #port: 7890 2 | #socks-port: 7891 3 | #redir-port: 7892 4 | #tproxy-port: 7893 5 | mixed-port: 7890 6 | 7 | #authentication: 8 | # - "user1:pass1" 9 | # - "user2:pass2" 10 | 11 | allow-lan: false 12 | bind-address: '*' 13 | mode: rule 14 | log-level: silent 15 | ipv6: false 16 | 17 | external-controller: 127.0.0.1:9090 18 | external-ui: yacd 19 | 20 | #secret: "" 21 | 22 | #interface-name: en0 23 | 24 | hosts: 25 | #'*.clash.dev': 127.0.0.1 26 | #'.dev': 127.0.0.1 27 | #'alpha.clash.dev': '::1' 28 | 29 | profile: 30 | store-selected: true 31 | 32 | # ## tun 33 | dns: 34 | enable: true 35 | listen: 0.0.0.0:53 36 | ipv6: false 37 | #default-nameserver: 38 | # - 114.114.114.114 39 | # #- 8.8.8.8 40 | enhanced-mode: fake-ip 41 | nameserver: 42 | - 223.5.5.5 43 | - 114.114.114.114 44 | #- 8.8.8.8 45 | #- tls://dns.rubyfish.cn:853 # DNS over TLS 46 | #- https://1.1.1.1/dns-query # DNS over HTTPS 47 | fallback: 48 | - https://8888.google/dns-query 49 | - https://1.0.0.1/dns-query 50 | fallback-filter: 51 | geoip: true 52 | ipcidr: 53 | #- 240.0.0.0/4 54 | #domain: 55 | # - '+.google.com' 56 | # - '+.facebook.com' 57 | # - '+.youtube.com' 58 | # - "+.github.com" 59 | # - "+.githubusercontent.com" 60 | # - "+.googlevideo.com" 61 | 62 | tun: 63 | enable: true 64 | stack: system # or gvisor 65 | dns-hijack: 66 | - any:53 67 | - tcp://any:53 68 | auto-route: true # manage `ip route` and `ip rules` 69 | auto-redir: true # manage nftable REDIRECT 70 | auto-detect-interface: true # conflict with interface-name 71 | -------------------------------------------------------------------------------- /data/config.ini: -------------------------------------------------------------------------------- 1 | [main] 2 | sc_host = sub.xeton.dev 3 | tun_mode = True 4 | -------------------------------------------------------------------------------- /data/profiles/example_url: -------------------------------------------------------------------------------- 1 | https://bulinkbulink.com/freefq/free/master/v2 2 | -------------------------------------------------------------------------------- /data/tpl/proxy_provider_urls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanChane/clash-auto/545d5e4a7e028e54b5d305ff36e26bbf565b8360/data/tpl/proxy_provider_urls -------------------------------------------------------------------------------- /data/tpl/tpl_basic_multi_pp.yaml: -------------------------------------------------------------------------------- 1 | #port: 7890 2 | #socks-port: 7891 3 | #redir-port: 7892 4 | #tproxy-port: 7893 5 | mixed-port: 7890 6 | 7 | #authentication: 8 | # - "user1:pass1" 9 | # - "user2:pass2" 10 | 11 | allow-lan: false 12 | bind-address: '*' 13 | mode: rule 14 | log-level: silent 15 | ipv6: false 16 | 17 | external-controller: 127.0.0.1:9090 18 | external-ui: yacd 19 | 20 | #secret: "" 21 | 22 | #interface-name: en0 23 | 24 | hosts: 25 | #'*.clash.dev': 127.0.0.1 26 | #'.dev': 127.0.0.1 27 | #'alpha.clash.dev': '::1' 28 | 29 | profile: 30 | store-selected: true 31 | 32 | # ## tun 33 | dns: 34 | enable: true 35 | listen: 0.0.0.0:53 36 | ipv6: false 37 | #default-nameserver: 38 | # - 114.114.114.114 39 | # #- 8.8.8.8 40 | enhanced-mode: fake-ip 41 | nameserver: 42 | - 223.5.5.5 43 | - 114.114.114.114 44 | #- 8.8.8.8 45 | #- tls://dns.rubyfish.cn:853 # DNS over TLS 46 | #- https://1.1.1.1/dns-query # DNS over HTTPS 47 | fallback: 48 | - https://8888.google/dns-query 49 | - https://1.0.0.1/dns-query 50 | fallback-filter: 51 | geoip: true 52 | ipcidr: 53 | #- 240.0.0.0/4 54 | #domain: 55 | # - '+.google.com' 56 | # - '+.facebook.com' 57 | # - '+.youtube.com' 58 | # - '+.github.com' 59 | # - '+.githubusercontent.com' 60 | # - '+.googlevideo.com' 61 | 62 | tun: 63 | enable: true 64 | stack: system # or gvisor 65 | dns-hijack: 66 | - any:53 67 | - tcp://any:53 68 | auto-route: true # manage `ip route` and `ip rules` 69 | auto-redir: true # manage nftable REDIRECT 70 | auto-detect-interface: true # conflict with interface-name 71 | 72 | proxy-groups: 73 | - name: "Entry" 74 | type: select 75 | proxies: 76 | - AllAuto 77 | - AllSelect 78 | - 79 | - 78 | url: http://www.gstatic.com/generate_204 79 | interval: 300 80 | 81 | - name: "Select" 82 | tpl_param: 83 | providers: ["provider"] 84 | type: select 85 | use: null 86 | url: http://www.gstatic.com/generate_204 87 | interval: 300 88 | 89 | - name: "Auto" 90 | tpl_param: 91 | providers: ["provider"] 92 | type: url-test 93 | use: null 94 | url: http://www.gstatic.com/generate_204 95 | interval: 300 96 | 97 | - name: "Entry-RuleMode" 98 | type: select 99 | proxies: 100 | - DIRECT 101 | - Entry 102 | 103 | - name: "Entry-LastMatch" 104 | type: select 105 | proxies: 106 | - Entry 107 | - DIRECT 108 | 109 | proxy-providers: 110 | provider: 111 | tpl_param: 112 | type: http 113 | url: null 114 | path: null 115 | interval: 3600 116 | health-check: 117 | enable: true 118 | interval: 600 119 | url: http://www.gstatic.com/generate_204 120 | 121 | rule-providers: 122 | reject: 123 | type: http 124 | behavior: domain 125 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt" 126 | path: ./ruleset/reject.yaml 127 | interval: 86400 128 | 129 | icloud: 130 | type: http 131 | behavior: domain 132 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/icloud.txt" 133 | path: ./ruleset/icloud.yaml 134 | interval: 86400 135 | 136 | apple: 137 | type: http 138 | behavior: domain 139 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/apple.txt" 140 | path: ./ruleset/apple.yaml 141 | interval: 86400 142 | 143 | #google: 144 | # type: http 145 | # behavior: domain 146 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt" 147 | # path: ./ruleset/google.yaml 148 | # interval: 86400 149 | 150 | #proxy: 151 | # type: http 152 | # behavior: domain 153 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt" 154 | # path: ./ruleset/proxy.yaml 155 | # interval: 86400 156 | 157 | direct: 158 | type: http 159 | behavior: domain 160 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/direct.txt" 161 | path: ./ruleset/direct.yaml 162 | interval: 86400 163 | 164 | private: 165 | type: http 166 | behavior: domain 167 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/private.txt" 168 | path: ./ruleset/private.yaml 169 | interval: 86400 170 | 171 | gfw: 172 | type: http 173 | behavior: domain 174 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt" 175 | path: ./ruleset/gfw.yaml 176 | interval: 86400 177 | 178 | #greatfire: 179 | # type: http 180 | # behavior: domain 181 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/greatfire.txt" 182 | # path: ./ruleset/greatfire.yaml 183 | # interval: 86400 184 | 185 | #tld-not-cn: 186 | # type: http 187 | # behavior: domain 188 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/tld-not-cn.txt" 189 | # path: ./ruleset/tld-not-cn.yaml 190 | # interval: 86400 191 | 192 | telegramcidr: 193 | type: http 194 | behavior: ipcidr 195 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/telegramcidr.txt" 196 | path: ./ruleset/telegramcidr.yaml 197 | interval: 86400 198 | 199 | cncidr: 200 | type: http 201 | behavior: ipcidr 202 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt" 203 | path: ./ruleset/cncidr.yaml 204 | interval: 86400 205 | 206 | lancidr: 207 | type: http 208 | behavior: ipcidr 209 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/lancidr.txt" 210 | path: ./ruleset/lancidr.yaml 211 | interval: 86400 212 | 213 | #applications: 214 | # type: http 215 | # behavior: classical 216 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/applications.txt" 217 | # path: ./ruleset/applications.yaml 218 | # interval: 86400 219 | 220 | rules: 221 | # ## Basic 222 | #- PROCESS-NAME,v2ray,DIRECT 223 | #- PROCESS-NAME,v2ray.exe,DIRECT 224 | - DOMAIN,clash.razord.top,DIRECT 225 | - DOMAIN,yacd.haishan.me,DIRECT 226 | - DOMAIN-SUFFIX,cn.bing.com,DIRECT 227 | - DOMAIN-SUFFIX,bing.com,Entry 228 | - DOMAIN,aur.archlinux.org,Entry 229 | #- DOMAIN-KEYWORD,speedtest,Entry 230 | 231 | # ## Sub Convert 232 | #- DOMAIN-SUFFIX,suo.yt,Entry 233 | #- DOMAIN-SUFFIX,bianyuan.xyz,Entry 234 | #- DOMAIN-SUFFIX,sub.xeton.dev,Entry 235 | #- DOMAIN-SUFFIX,bihai.cf,Entry 236 | #- DOMAIN-SUFFIX,sub.id9.cc,Entry 237 | 238 | # ## Clash Rules 239 | - RULE-SET,private,DIRECT 240 | - RULE-SET,reject,REJECT 241 | - RULE-SET,gfw,Entry 242 | - RULE-SET,telegramcidr,Entry 243 | - RULE-SET,cncidr,Entry-RuleMode 244 | - RULE-SET,direct,Entry-RuleMode 245 | - RULE-SET,icloud,Entry-RuleMode 246 | - RULE-SET,apple,Entry-RuleMode 247 | - RULE-SET,lancidr,DIRECT 248 | #- GEOIP,LAN,DIRECT # [why need it if has lancidr](https://github.com/Loyalsoldier/clash-rules/issues/50) 249 | #- GEOIP,CN,Entry-RuleMode # [why need it if has cncidr](https://github.com/Loyalsoldier/clash-rules/issues/192) 250 | 251 | - MATCH,Entry-LastMatch 252 | -------------------------------------------------------------------------------- /data/tpl/tpl_chatgpt_multi_pp.yaml: -------------------------------------------------------------------------------- 1 | #port: 7890 2 | #socks-port: 7891 3 | #redir-port: 7892 4 | #tproxy-port: 7893 5 | mixed-port: 7890 6 | 7 | #authentication: 8 | # - "user1:pass1" 9 | # - "user2:pass2" 10 | 11 | allow-lan: false 12 | bind-address: '*' 13 | mode: rule 14 | log-level: silent 15 | ipv6: false 16 | 17 | external-controller: 127.0.0.1:9090 18 | external-ui: yacd 19 | 20 | #secret: "" 21 | 22 | #interface-name: en0 23 | 24 | hosts: 25 | #'*.clash.dev': 127.0.0.1 26 | #'.dev': 127.0.0.1 27 | #'alpha.clash.dev': '::1' 28 | 29 | profile: 30 | store-selected: true 31 | 32 | # ## tun 33 | dns: 34 | enable: true 35 | listen: 0.0.0.0:53 36 | ipv6: false 37 | #default-nameserver: 38 | # - 114.114.114.114 39 | # #- 8.8.8.8 40 | enhanced-mode: fake-ip 41 | nameserver: 42 | - 223.5.5.5 43 | - 114.114.114.114 44 | #- 8.8.8.8 45 | #- tls://dns.rubyfish.cn:853 # DNS over TLS 46 | #- https://1.1.1.1/dns-query # DNS over HTTPS 47 | fallback: 48 | - https://8888.google/dns-query 49 | - https://1.0.0.1/dns-query 50 | fallback-filter: 51 | geoip: true 52 | ipcidr: 53 | #- 240.0.0.0/4 54 | #domain: 55 | # - '+.google.com' 56 | # - '+.facebook.com' 57 | # - '+.youtube.com' 58 | # - '+.github.com' 59 | # - '+.githubusercontent.com' 60 | # - '+.googlevideo.com' 61 | 62 | tun: 63 | enable: true 64 | stack: system # or gvisor 65 | dns-hijack: 66 | - any:53 67 | - tcp://any:53 68 | auto-route: true # manage `ip route` and `ip rules` 69 | auto-redir: true # manage nftable REDIRECT 70 | auto-detect-interface: true # conflict with interface-name 71 | 72 | proxy-groups: 73 | # ## Entry 74 | - name: "Entry" 75 | type: select 76 | proxies: 77 | - AllAuto 78 | - AllSelect 79 | - 80 | - 78 | url: http://www.gstatic.com/generate_204 79 | interval: 300 80 | 81 | - name: "Entry-ChatGpt" 82 | type: select 83 | proxies: 84 | - 85 | url: https://api.openai.com/v1/ping 86 | interval: 300 87 | 88 | - name: "Select-Chatgpt" 89 | tpl_param: 90 | providers: ["chatgpt"] 91 | type: select 92 | use: null 93 | url: https://api.openai.com/v1/ping 94 | interval: 300 95 | 96 | - name: "Select" 97 | tpl_param: 98 | providers: ["provider"] 99 | type: select 100 | use: null 101 | url: http://www.gstatic.com/generate_204 102 | interval: 300 103 | 104 | - name: "Auto" 105 | tpl_param: 106 | providers: ["provider"] 107 | type: url-test 108 | use: null 109 | url: http://www.gstatic.com/generate_204 110 | interval: 300 111 | 112 | - name: "Entry-RuleMode" 113 | type: select 114 | proxies: 115 | - DIRECT 116 | - Entry 117 | 118 | - name: "Entry-LastMatch" 119 | type: select 120 | proxies: 121 | - Entry 122 | - DIRECT 123 | 124 | proxy-providers: 125 | provider: 126 | tpl_param: 127 | type: http 128 | url: null 129 | path: null 130 | interval: 3600 131 | health-check: 132 | enable: true 133 | interval: 600 134 | url: http://www.gstatic.com/generate_204 135 | 136 | chatgpt: 137 | tpl_param: 138 | type: http 139 | url: null 140 | path: null 141 | interval: 3600 142 | health-check: 143 | enable: true 144 | interval: 600 145 | url: https://api.openai.com/v1/ping 146 | 147 | rule-providers: 148 | reject: 149 | type: http 150 | behavior: domain 151 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt" 152 | path: ./ruleset/reject.yaml 153 | interval: 86400 154 | 155 | icloud: 156 | type: http 157 | behavior: domain 158 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/icloud.txt" 159 | path: ./ruleset/icloud.yaml 160 | interval: 86400 161 | 162 | apple: 163 | type: http 164 | behavior: domain 165 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/apple.txt" 166 | path: ./ruleset/apple.yaml 167 | interval: 86400 168 | 169 | #google: 170 | # type: http 171 | # behavior: domain 172 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt" 173 | # path: ./ruleset/google.yaml 174 | # interval: 86400 175 | 176 | #proxy: 177 | # type: http 178 | # behavior: domain 179 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt" 180 | # path: ./ruleset/proxy.yaml 181 | # interval: 86400 182 | 183 | direct: 184 | type: http 185 | behavior: domain 186 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/direct.txt" 187 | path: ./ruleset/direct.yaml 188 | interval: 86400 189 | 190 | private: 191 | type: http 192 | behavior: domain 193 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/private.txt" 194 | path: ./ruleset/private.yaml 195 | interval: 86400 196 | 197 | gfw: 198 | type: http 199 | behavior: domain 200 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt" 201 | path: ./ruleset/gfw.yaml 202 | interval: 86400 203 | 204 | #greatfire: 205 | # type: http 206 | # behavior: domain 207 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/greatfire.txt" 208 | # path: ./ruleset/greatfire.yaml 209 | # interval: 86400 210 | 211 | #tld-not-cn: 212 | # type: http 213 | # behavior: domain 214 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/tld-not-cn.txt" 215 | # path: ./ruleset/tld-not-cn.yaml 216 | # interval: 86400 217 | 218 | telegramcidr: 219 | type: http 220 | behavior: ipcidr 221 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/telegramcidr.txt" 222 | path: ./ruleset/telegramcidr.yaml 223 | interval: 86400 224 | 225 | cncidr: 226 | type: http 227 | behavior: ipcidr 228 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt" 229 | path: ./ruleset/cncidr.yaml 230 | interval: 86400 231 | 232 | lancidr: 233 | type: http 234 | behavior: ipcidr 235 | url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/lancidr.txt" 236 | path: ./ruleset/lancidr.yaml 237 | interval: 86400 238 | 239 | #applications: 240 | # type: http 241 | # behavior: classical 242 | # url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/applications.txt" 243 | # path: ./ruleset/applications.yaml 244 | # interval: 86400 245 | 246 | rules: 247 | # ## Basic 248 | #- PROCESS-NAME,v2ray,DIRECT 249 | #- PROCESS-NAME,v2ray.exe,DIRECT 250 | - DOMAIN,clash.razord.top,DIRECT 251 | - DOMAIN,yacd.haishan.me,DIRECT 252 | - DOMAIN-SUFFIX,cn.bing.com,DIRECT 253 | - DOMAIN-SUFFIX,bing.com,Entry 254 | - DOMAIN-SUFFIX,openai.com,Entry-ChatGpt 255 | - DOMAIN,aur.archlinux.org,Entry 256 | #- DOMAIN-KEYWORD,speedtest,Entry 257 | 258 | # ## Sub Convert 259 | #- DOMAIN-SUFFIX,suo.yt,Entry 260 | #- DOMAIN-SUFFIX,bianyuan.xyz,Entry 261 | #- DOMAIN-SUFFIX,sub.xeton.dev,Entry 262 | #- DOMAIN-SUFFIX,bihai.cf,Entry 263 | #- DOMAIN-SUFFIX,sub.id9.cc,Entry 264 | 265 | # ## Clash Rules 266 | - RULE-SET,private,DIRECT 267 | - RULE-SET,reject,REJECT 268 | - RULE-SET,gfw,Entry 269 | - RULE-SET,telegramcidr,Entry 270 | - RULE-SET,cncidr,Entry-RuleMode 271 | - RULE-SET,direct,Entry-RuleMode 272 | - RULE-SET,icloud,Entry-RuleMode 273 | - RULE-SET,apple,Entry-RuleMode 274 | - RULE-SET,lancidr,DIRECT 275 | #- GEOIP,LAN,DIRECT # [why need it if has lancidr](https://github.com/Loyalsoldier/clash-rules/issues/50) 276 | #- GEOIP,CN,Entry-RuleMode # [why need it if has cncidr](https://github.com/Loyalsoldier/clash-rules/issues/192) 277 | 278 | - MATCH,Entry-LastMatch 279 | -------------------------------------------------------------------------------- /final_clash_config.yaml: -------------------------------------------------------------------------------- 1 | #port: 7890 2 | #socks-port: 7891 3 | #redir-port: 7892 4 | #tproxy-port: 7893 5 | mixed-port: 7890 6 | 7 | #authentication: 8 | # - "user1:pass1" 9 | # - "user2:pass2" 10 | 11 | allow-lan: false 12 | bind-address: '*' 13 | mode: rule 14 | log-level: silent 15 | ipv6: false 16 | 17 | external-controller: 127.0.0.1:9090 18 | external-ui: yacd 19 | 20 | #secret: "" 21 | 22 | #interface-name: en0 23 | 24 | hosts: 25 | #'*.clash.dev': 127.0.0.1 26 | #'.dev': 127.0.0.1 27 | #'alpha.clash.dev': '::1' 28 | 29 | profile: 30 | store-selected: true 31 | 32 | # ## tun 33 | dns: 34 | enable: true 35 | listen: 0.0.0.0:53 36 | ipv6: false 37 | #default-nameserver: 38 | # - 114.114.114.114 39 | # #- 8.8.8.8 40 | enhanced-mode: fake-ip 41 | nameserver: 42 | - 223.5.5.5 43 | - 114.114.114.114 44 | #- 8.8.8.8 45 | #- tls://dns.rubyfish.cn:853 # DNS over TLS 46 | #- https://1.1.1.1/dns-query # DNS over HTTPS 47 | fallback: 48 | - https://8888.google/dns-query 49 | - https://1.0.0.1/dns-query 50 | fallback-filter: 51 | geoip: true 52 | ipcidr: 53 | #- 240.0.0.0/4 54 | #domain: 55 | # - '+.google.com' 56 | # - '+.facebook.com' 57 | # - '+.youtube.com' 58 | # - "+.github.com" 59 | # - "+.githubusercontent.com" 60 | # - "+.googlevideo.com" 61 | 62 | tun: 63 | enable: true 64 | stack: system # or gvisor 65 | dns-hijack: 66 | - any:53 67 | - tcp://any:53 68 | auto-route: true # manage `ip route` and `ip rules` 69 | auto-redir: true # manage nftable REDIRECT 70 | auto-detect-interface: true # conflict with interface-name 71 | -------------------------------------------------------------------------------- /screenshots/clashauto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohanChane/clash-auto/545d5e4a7e028e54b5d305ff36e26bbf565b8360/screenshots/clashauto.png --------------------------------------------------------------------------------