diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..1ada173 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,37 @@ +# Copilot Instructions for config_convert + +## Project purpose + +- Converts multiple upstream Dev-Sidecar configs into one merged config published at assets/final_config.json. +- Merge order matters: default_remote → 8odream → Sheas Cealer → manual, then remove excluded_domains entries (left-to-right precedence). + +## Key modules + +- src/main.py: CLI entrypoint (`--debug` enables verbose logging). Loads excluded_domains.json5, instantiates configs, merges via ExtendedDict operators, writes sorted JSON to final_config_path. +- src/Config.py: Config base class (download helpers using GitHub mirrors, JSON5 parsing). Subclasses: RemoteConfig (generic URL), GithubConfig (GitHub path with mirrors), LocalConfig (local JSON5), SheasCealerConfig (transforms Cealer list into Dev-Sidecar `server.intercepts` + `preSetIpList`, skipping IPv6 when header.skip_IPv6). +- src/ExtendedDict.py: `__add__` deep-merges dicts iteratively (rewrite=True default overwrites existing keys); `__sub__` removes keys recursively; returned type always ExtendedDict for chaining. +- src/utils.py: `is_ipv6_address` handles bracketed literals; `sort_json_object` sorts mapping keys by length desc then alphabetically to stabilize output; `show_raw_text_for_debugging` truncates to 500 chars. +- src/header.py: Central paths (manual_config.json5, excluded_domains.json5, final_config.json) and mirror lists; requires Python 3.14+ per pyproject.toml. + +## Behaviors & conventions + +- Merging relies on ExtendedDict semantics; if you change precedence, adjust ordering in src/main.py. +- Excluded domains are removed after all additions (`final_config = a + b + c + d - excluded_domains`). +- SheasCealer conversion: domain list cleaned (strips $/#, skips entries containing ^); each domain is processed individually; empty SNI becomes "none"; empty target becomes 127.0.0.1; only non-IPv6 targets added to `preSetIpList` when skip_IPv6. +- Output serialization: ensure_ascii=False, indent=2, and key sorting via sort_json_object before writing (match existing format for diffs/automation). +- 对话与代码注释请使用中文,日志输出维持既有英文格式。 + +## Local workflow + +- Setup: Python 3.14+, `pip install -r requirements.txt` (exported from uv). json5 and requests are required. +- Run: `python src/main.py` (add `--debug` for verbose). Output writes to assets/final_config.json alongside manual and excluded files in assets/. + +## Extending safely + +- When adding new sources, prefer new Config subclasses or reuse GithubConfig/RemoteConfig; always call Config.download with mirrors to avoid single-point failures. +- If changing save logic, consider reusing Config.save to keep formatting consistent with main.py TODO. +- Maintain mirror lists in header.py; skip_IPv6 controls IPv6 handling globally. + +## CI/Git flow + +- dev branch for changes; main is protected/production. Actions push merged config to main using PAT. Keep output deterministic (sorting) to avoid noisy diffs. diff --git a/README.md b/README.md index 18d154e..860a7a3 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ 把Sheas Cealer的配置文件转换成Dev-Sidecar的配置文件,并支持与其他Dev-Sidecar配置合并。目前由GitHub Actions自动执行,当前每30分钟更新一次配置。 -## 用法: -把https://cute-omega.github.io/other-assets/ds-config.json 填入dev-sidecar的个人远程配置,然后点击“更新远程配置”以立即生效。之后dev-sidecar会在每次启动时自动更新配置。 +## 用法 -## 脚本当前工作流: +把 填入dev-sidecar的个人远程配置,然后点击“更新远程配置”以立即生效。之后dev-sidecar会在每次启动时自动更新配置。 + +## 脚本当前工作流 `default_remote.config + 8odream.config + sheas_cealer.config + manual.config - excluded_domains` @@ -33,6 +34,7 @@ python3 src/main.py ``` ## 开发 + 请Fork dev分支,更新也请向dev分支发PR。 推送PR前请在本地测试无bug后进行。 @@ -43,5 +45,6 @@ main分支仅用于生产用途。 本仓库使用GitHub Actions自动运行配置转换并推送到main分支。由于main分支有分支保护规则,工作流使用Personal Access Token (PAT)来绕过这些规则。 -### 所需的Secrets配置: +### 所需的Secrets配置 + - `PAT`: 具有repo权限的Personal Access Token,用于向main分支推送更改 diff --git a/assets/final_config.json b/assets/final_config.json index e24d81f..dcf61cd 100644 --- a/assets/final_config.json +++ b/assets/final_config.json @@ -58,9 +58,6 @@ }, "server": { "preSetIpList": { - "(*wikipedia.org|*wikinews.org|*wiktionary.org|*wikibooks.org|*wikiversity.org|*wikidata.org|*wikiquote.org|*wikivoyage.org|*wikifunctions.org|*wikisource.org|*mediawiki.org|w.wiki|*.w.wiki)": { - "185.15.59.224": true - }, "^.*(youtube.com|gstatic.com|youtube.nocookie.com|youtu.be|ggpht.com|i.ytimg.com|blogger.com|doodles.google|about.google|android.com)$": { "142.251.189.206": { "desc": "自动地区官方" @@ -69,31 +66,10 @@ "desc": "阿里云中转" } }, - "(*google*|*google.com|*gstatic.com|*youtube.com|*youtu.be|*.ggpht.com|i.ytimg.com|*youtube-nocookie.com|*blogger.com|*android.com)": { - "34.49.133.3": true - }, "^.*(wikipedia|wikimedia|wikinews|wiktionary|wikiversity|wikidata|wikiquote|wikivoyage|wikifunctions).org$": { "208.80.153.224": true, "185.15.58.224": true }, - "(*telegram.org|telegram.me|*tg.dev|t.me|*.t.me|*telesco.pe)": { - "93.183.68.61": true - }, - "(*reddit.com|*redditstatic.com|*redd.it|*redditmedia.com)": { - "146.75.33.140": true - }, - "(*facebook.com|*fbcdn.net|business.whatsapp.com)": { - "157.240.22.169": true - }, - "(zws1*.web.telegram.org|zws3*.web.telegram.org)": { - "149.154.174.200": true - }, - "(zws2*.web.telegram.org|zws4*.web.telegram.org)": { - "93.183.68.61": true - }, - "(*instagram.com|*instagr.am|ig.me|*.ig.me)": { - "157.240.27.174": true - }, "^(aistudio|.*pa.clients6|apis).google.com$": { "8.137.102.117": { "desc": "AI Studio相关,阿里云,解锁地区限制" @@ -107,15 +83,6 @@ "desc": "阿里云中转" } }, - "(support.github.com|resources.github.com)": { - "185.199.108.133": true - }, - "(f-droid.org|www.f-droid.org|fdroid.org)": { - "37.218.243.72": true - }, - "(*twitch.tv|m.twitch.tv|gql.twitch.tv)": { - "151.101.194.167": true - }, "internal-api.virginia.labs.lumalabs.ai": { "34.204.141.236": true }, @@ -132,20 +99,11 @@ "(*account*|scholar).google.com": { "172.217.204.206": true }, - "(*whatsapp.com|*whatsapp.net)": { - "157.240.225.60": true - }, "scholar.googleusercontent.com": { "142.251.190.206": true, "172.217.204.206": true, "172.253.122.206": true }, - "(*pinterest.com|*pinimg.com)": { - "151.101.0.84": true - }, - "(*z-library.sk|*z-lib.help)": { - "176.123.7.105": true - }, "graphql.api.dailymotion.com": { "34.84.14.157": true }, @@ -193,18 +151,6 @@ "disney.*.edge.bamgrid.com": { "2001:67c:2960:6464::36e6:4744": true }, - "(*bbci.co.uk|*bbc.co.uk)": { - "23.77.21.232": true - }, - "(*xvideos.com|*xnxx.com)": { - "185.88.181.3": true - }, - "(*nytimes.com|*nyt.com)": { - "146.75.117.164": true - }, - "(*pixiv.net|*fanbox.cc)": { - "210.140.139.155": true - }, "*patreonusercontent.com": { "104.18.70.106": true }, @@ -261,6 +207,21 @@ "23.219.73.99": true, "23.46.197.62": true }, + "zws1*.web.telegram.org": { + "149.154.174.200": true + }, + "zws2*.web.telegram.org": { + "93.183.68.61": true + }, + "zws3*.web.telegram.org": { + "149.154.174.200": true + }, + "zws4*.web.telegram.org": { + "93.183.68.61": true + }, + "*youtube-nocookie.com": { + "34.49.133.3": true + }, "account-api.proton.me": { "185.70.42.20": true }, @@ -304,6 +265,9 @@ "community.github.com": { "140.82.114.17": true }, + "resources.github.com": { + "185.199.108.133": true + }, "upload.wikimedia.org": { "208.80.153.240": true, "208.80.154.240": true, @@ -347,6 +311,9 @@ "*onedrive.live.com": { "13.107.42.13": true }, + "*wikifunctions.org": { + "185.15.59.224": true + }, "calendar.proton.me": { "185.70.42.39": true }, @@ -362,8 +329,8 @@ "passport.twitch.tv": { "3.167.200.113": true }, - "(ok.ru|www.ok.ru)": { - "5.61.23.11": true + "support.github.com": { + "185.199.108.133": true }, "*.steamstatic.com": { "151.101.111.52": true, @@ -442,6 +409,9 @@ "*sukebei.nyaa.si": { "198.251.89.38": true }, + "*wikiversity.org": { + "185.15.59.224": true + }, "*xhamster42.desi": { "104.17.35.109": true }, @@ -500,6 +470,15 @@ "*pixeldrain.com": { "103.107.198.191": true }, + "*wikisource.org": { + "185.15.59.224": true + }, + "*wikivoyage.org": { + "185.15.59.224": true + }, + "*wiktionary.org": { + "185.15.59.224": true + }, "docs.github.com": { "185.199.110.154": true }, @@ -517,6 +496,9 @@ "sp.nicovideo.jp": { "3.167.200.113": true }, + "www.f-droid.org": { + "37.218.243.72": true + }, "*apkmirror.com": { "104.17.67.215": true }, @@ -527,12 +509,24 @@ "157.240.236.174": true, "157.240.27.174": true }, + "*mediawiki.org": { + "185.15.59.224": true + }, "*pinterest.com": { "151.101.0.84": true }, "*startpage.com": { "67.63.58.139": true }, + "*wikibooks.org": { + "185.15.59.224": true + }, + "*wikipedia.org": { + "185.15.59.224": true + }, + "*wikiquote.org": { + "185.15.59.224": true + }, "_google_backup": { "142.251.189.206": true, "142.251.190.206": true, @@ -632,6 +626,12 @@ "*whatsapp.net": { "157.240.225.60": true }, + "*wikidata.org": { + "185.15.59.224": true + }, + "*wikinews.org": { + "185.15.59.224": true + }, "*xhamster.com": { "104.17.35.109": true }, @@ -644,6 +644,9 @@ "gql.twitch.tv": { "151.101.194.167": true }, + "*android.com": { + "34.49.133.3": true + }, "*archive.org": { "207.241.237.2": true }, @@ -653,6 +656,9 @@ "*bilibili.tv": { "2001:67c:2960:6464::6797:9785": true }, + "*blogger.com": { + "34.49.133.3": true + }, "*discord.com": { "162.159.136.232": true }, @@ -662,6 +668,9 @@ "*f-droid.org": { "37.218.243.72": true }, + "*gstatic.com": { + "34.49.133.3": true + }, "*lumalabs.ai": { "76.76.21.21": true }, @@ -687,6 +696,9 @@ "*xvideos.com": { "185.88.181.3": true }, + "*youtube.com": { + "34.49.133.3": true + }, "_*.yahoo.com": { "2001:67c:2960:6464::b4de:6a0b": true }, @@ -694,7 +706,8 @@ "210.140.139.155": true }, "*.ggpht.com": { - "8.137.102.117": true + "8.137.102.117": true, + "34.49.133.3": true }, "*bbci.co.uk": { "23.77.21.232": true @@ -711,6 +724,9 @@ "*github.com": { "20.27.177.113": true }, + "*google.com": { + "34.49.133.3": true + }, "*instagr.am": { "157.240.236.174": true, "157.240.27.174": true @@ -744,6 +760,9 @@ "210.140.139.183": true, "210.140.139.184": true }, + "f-droid.org": { + "37.218.243.72": true + }, "i.pximg.net": { "210.140.139.132": true, "210.140.139.133": true, @@ -755,11 +774,15 @@ "203.137.29.49": true }, "i.ytimg.com": { - "8.137.102.117": true + "8.137.102.117": true, + "34.49.133.3": true }, "m.twitch.tv": { "151.101.194.167": true }, + "telegram.me": { + "93.183.68.61": true + }, "*.youtu.be": { "8.137.102.117": true }, @@ -848,12 +871,21 @@ "*xnxx.com": { "185.88.181.3": true }, + "*youtu.be": { + "34.49.133.3": true + }, "www.ok.ru": { "5.61.23.11": true }, + "*.w.wiki": { + "185.15.59.224": true + }, "*bbc.com": { "146.75.36.81": true }, + "*google*": { + "34.49.133.3": true + }, "*itch.io": { "142.171.75.212": true }, @@ -898,6 +930,9 @@ "*ok.ru": { "5.61.23.30": true }, + "w.wiki": { + "185.15.59.224": true + }, "ig.me": { "157.240.236.174": true, "157.240.27.174": true @@ -910,21 +945,11 @@ } }, "intercepts": { - "(*wikipedia.org|*wikinews.org|*wiktionary.org|*wikibooks.org|*wikiversity.org|*wikidata.org|*wikiquote.org|*wikivoyage.org|*wikifunctions.org|*wikisource.org|*mediawiki.org|w.wiki|*.w.wiki)": { - ".*": { - "sni": "none" - } - }, "^.*(youtube.com|gstatic.com|youtube.nocookie.com|youtu.be|ggpht.com|i.ytimg.com|blogger.com|doodles.google|about.google|android.com)$": { ".*": { "sni": "www.google.cn" } }, - "(*google*|*google.com|*gstatic.com|*youtube.com|*youtu.be|*.ggpht.com|i.ytimg.com|*youtube-nocookie.com|*blogger.com|*android.com)": { - ".*": { - "sni": "g.cn" - } - }, "^.*(wikipedia|wikimedia|wikinews|wiktionary|wikiversity|wikidata|wikiquote|wikivoyage|wikifunctions|wikibooks).org$": { ".*": { "sni": "mediawiki.org" @@ -941,57 +966,12 @@ "sni": "www.baidu.com" } }, - "(*telegram.org|telegram.me|*tg.dev|t.me|*.t.me|*telesco.pe)": { - ".*": { - "sni": "none" - } - }, - "(*reddit.com|*redditstatic.com|*redd.it|*redditmedia.com)": { - ".*": { - "sni": "none" - } - }, - "(*facebook.com|*fbcdn.net|business.whatsapp.com)": { - ".*": { - "sni": "none" - } - }, - "(zws1*.web.telegram.org|zws3*.web.telegram.org)": { - ".*": { - "sni": "none" - } - }, - "(zws2*.web.telegram.org|zws4*.web.telegram.org)": { - ".*": { - "sni": "none" - } - }, "^(?!assets|gql|vod-secure|panels)\\.twitch\\.tv$": { ".*": { "desc": "聊天需要 wss://irc-ws.chat.twitch.tv/", "sni": "cloudfront.net" } }, - "(*instagram.com|*instagr.am|ig.me|*.ig.me)": { - ".*": { - "sni": "none" - } - }, - "(support.github.com|resources.github.com)": { - ".*": { - "sni": "none" - } - }, - "(f-droid.org|www.f-droid.org|fdroid.org)": { - ".*": { - "sni": "none" - } - }, - "(*twitch.tv|m.twitch.tv|gql.twitch.tv)": { - ".*": { - "sni": "none" - } - }, "internal-api.virginia.labs.lumalabs.ai": { ".*": { "sni": "www.baidu.com" @@ -1025,21 +1005,6 @@ "proxy": "fonts.googleapis.cn" } }, - "(*whatsapp.com|*whatsapp.net)": { - ".*": { - "sni": "none" - } - }, - "(*pinterest.com|*pinimg.com)": { - ".*": { - "sni": "none" - } - }, - "(*z-library.sk|*z-lib.help)": { - ".*": { - "sni": "none" - } - }, "graphql.api.dailymotion.com": { ".*": { "sni": "www.baidu.com" @@ -1068,26 +1033,6 @@ "sni": "disney.images.edge.bamgrid.com" } }, - "(*bbci.co.uk|*bbc.co.uk)": { - ".*": { - "sni": "none" - } - }, - "(*xvideos.com|*xnxx.com)": { - ".*": { - "sni": "none" - } - }, - "(*nytimes.com|*nyt.com)": { - ".*": { - "sni": "none" - } - }, - "(*pixiv.net|*fanbox.cc)": { - ".*": { - "sni": "pixivision.net" - } - }, "*.githubusercontent.com": { ".*": { "requestReplace": { @@ -1141,6 +1086,31 @@ "sni": "none" } }, + "zws1*.web.telegram.org": { + ".*": { + "sni": "none" + } + }, + "zws2*.web.telegram.org": { + ".*": { + "sni": "none" + } + }, + "zws3*.web.telegram.org": { + ".*": { + "sni": "none" + } + }, + "zws4*.web.telegram.org": { + ".*": { + "sni": "none" + } + }, + "*youtube-nocookie.com": { + ".*": { + "sni": "g.cn" + } + }, "account-api.proton.me": { ".*": { "sni": "www.baidu.com" @@ -1191,6 +1161,11 @@ "sni": "none" } }, + "resources.github.com": { + ".*": { + "sni": "none" + } + }, "upload.wikimedia.org": { ".*": { "sni": "none" @@ -1275,12 +1250,17 @@ "sni": "www.baidu.com" } }, + "*wikifunctions.org": { + ".*": { + "sni": "none" + } + }, "calendar.proton.me": { ".*": { "sni": "www.baidu.com" } }, - "(ok.ru|www.ok.ru)": { + "support.github.com": { ".*": { "sni": "none" } @@ -1371,6 +1351,11 @@ "sni": "www.baidu.com" } }, + "*wikiversity.org": { + ".*": { + "sni": "none" + } + }, "*xhamster42.desi": { ".*": { "sni": "zh.xhamster42.desi" @@ -1467,6 +1452,21 @@ "sni": "pixeldra.in" } }, + "*wikisource.org": { + ".*": { + "sni": "none" + } + }, + "*wikivoyage.org": { + ".*": { + "sni": "none" + } + }, + "*wiktionary.org": { + ".*": { + "sni": "none" + } + }, "_a5.behance.net": { ".*": { "sni": "a5.behance.net" @@ -1492,6 +1492,11 @@ "sni": "baidu.com" } }, + "www.f-droid.org": { + ".*": { + "sni": "none" + } + }, "www.gstatic.com": { "/recaptcha/.*": { "proxy": "www.recaptcha.net" @@ -1517,6 +1522,11 @@ "sni": "www.baidu.com" } }, + "*mediawiki.org": { + ".*": { + "sni": "none" + } + }, "*pinterest.com": { ".*": { "sni": "www.baidu.com" @@ -1527,6 +1537,21 @@ "sni": "www.baidu.com" } }, + "*wikibooks.org": { + ".*": { + "sni": "none" + } + }, + "*wikipedia.org": { + ".*": { + "sni": "none" + } + }, + "*wikiquote.org": { + ".*": { + "sni": "none" + } + }, "a5.behance.net": { ".*": { "sni": "a5.behance.net" @@ -1637,6 +1662,11 @@ "sni": "www.baidu.com" } }, + "*telegram.org": { + ".*": { + "sni": "none" + } + }, "*whatsapp.com": { ".*": { "sni": "www.baidu.com" @@ -1647,6 +1677,16 @@ "sni": "www.baidu.com" } }, + "*wikidata.org": { + ".*": { + "sni": "none" + } + }, + "*wikinews.org": { + ".*": { + "sni": "none" + } + }, "*xhamster.com": { ".*": { "sni": "zh.xhamster.com" @@ -1672,6 +1712,11 @@ "sni": "api.fanbox.cc" } }, + "gql.twitch.tv": { + ".*": { + "sni": "none" + } + }, "raw.incept.pw": { "^.*\\?DS_DOWNLOAD$": { "responseReplace": { @@ -1692,6 +1737,11 @@ "sni": "baidu.com" } }, + "*android.com": { + ".*": { + "sni": "g.cn" + } + }, "*archive.org": { ".*": { "sni": "www.baidu.com" @@ -1707,6 +1757,11 @@ "sni": "bilibili.tv" } }, + "*blogger.com": { + ".*": { + "sni": "g.cn" + } + }, "*discord.com": { ".*": { "sni": "none" @@ -1722,6 +1777,11 @@ "sni": "www.baidu.com" } }, + "*gstatic.com": { + ".*": { + "sni": "g.cn" + } + }, "*lumalabs.ai": { ".*": { "sni": "vercel.com" @@ -1762,6 +1822,11 @@ "sni": "www.baidu.com" } }, + "*youtube.com": { + ".*": { + "sni": "g.cn" + } + }, "cn.vuejs.org": { ".*": { "sni": "vuejs.org" @@ -1822,6 +1887,11 @@ "sni": "www.baidu.com" } }, + "*google.com": { + ".*": { + "sni": "g.cn" + } + }, "*instagr.am": { ".*": { "sni": "www.baidu.com" @@ -1842,6 +1912,11 @@ "sni": "www.baidu.com" } }, + "*telesco.pe": { + ".*": { + "sni": "none" + } + }, "*tumblr.com": { ".*": { "sni": "none" @@ -1862,6 +1937,11 @@ "sni": "claude.ai" } }, + "f-droid.org": { + ".*": { + "sni": "none" + } + }, "i.pximg.net": { ".*": { "requestReplace": { @@ -1878,6 +1958,16 @@ "sni": "baidu.com" } }, + "m.twitch.tv": { + ".*": { + "sni": "none" + } + }, + "telegram.me": { + ".*": { + "sni": "none" + } + }, "*.youtu.be": { ".*": { "sni": "baidu.com" @@ -1938,6 +2028,11 @@ "sni": "www.baidu.com" } }, + "*twitch.tv": { + ".*": { + "sni": "none" + } + }, "*vimeo.com": { ".*": { "sni": "www.baidu.com" @@ -2009,16 +2104,31 @@ "sni": "www.baidu.com" } }, + "*youtu.be": { + ".*": { + "sni": "g.cn" + } + }, "www.ok.ru": { ".*": { "sni": "www.baidu.com" } }, + "*.w.wiki": { + ".*": { + "sni": "none" + } + }, "*bbc.com": { ".*": { "sni": "www.baidu.com" } }, + "*google*": { + ".*": { + "sni": "g.cn" + } + }, "*itch.io": { ".*": { "sni": "none" @@ -2064,6 +2174,11 @@ "sni": "www.baidu.com" } }, + "*tg.dev": { + ".*": { + "sni": "none" + } + }, "*w.wiki": { ".*": { "sni": "mediawiki.org" @@ -2084,11 +2199,21 @@ "redirect": "www.reddit.com" } }, + "*.t.me": { + ".*": { + "sni": "none" + } + }, "*ok.ru": { ".*": { "sni": "www.baidu.com" } }, + "w.wiki": { + ".*": { + "sni": "none" + } + }, "ig.me": { ".*": { "sni": "www.baidu.com" @@ -2098,6 +2223,11 @@ ".*": { "sni": "www.baidu.com" } + }, + "t.me": { + ".*": { + "sni": "none" + } } }, "whiteList": { diff --git a/src/Config.py b/src/Config.py index b4bbd56..962f5a4 100644 --- a/src/Config.py +++ b/src/Config.py @@ -163,19 +163,16 @@ def __convert(self) -> ExtendedDict: for raw_domain in raw_domains if "^" not in raw_domain ] - domain_rules = "|".join(domains) - if len(domains) > 1: - domain_rules = f"({domain_rules})" - if domain_rules: + for domain in domains: intercepts = ds_config["server"].setdefault("intercepts", {}) - domain_dict = intercepts.setdefault(domain_rules, {}) + domain_dict = intercepts.setdefault(domain, {}) sni_dict = domain_dict.setdefault(".*", {}) sni_dict["sni"] = sni if skip_IPv6 and not is_IPv6: pre_set_ip_list = ds_config["server"].setdefault("preSetIpList", {}) - pre_set_domain_dict = pre_set_ip_list.setdefault(domain_rules, {}) + pre_set_domain_dict = pre_set_ip_list.setdefault(domain, {}) pre_set_domain_dict[target] = True return ds_config