Skip to main content

rinova_proxy_sdk/
builder.rs

1use crate::types::{ClashConfig, ProxyGroup, ProxyNode, RuleMode};
2
3const BUILTIN_RULES: &[&str] = &[
4    "DOMAIN-SUFFIX,ad.com,πŸ›‘ εΉΏε‘Šζ‹¦ζˆͺ",
5    "DOMAIN-SUFFIX,doubleclick.net,πŸ›‘ εΉΏε‘Šζ‹¦ζˆͺ",
6    "DOMAIN-SUFFIX,google.com,🌍 国倖网站",
7    "DOMAIN-SUFFIX,youtube.com,🌍 国倖网站",
8    "DOMAIN-SUFFIX,github.com,🌍 国倖网站",
9    "DOMAIN-SUFFIX,twitter.com,🌍 国倖网站",
10    "DOMAIN-SUFFIX,x.com,🌍 国倖网站",
11    "DOMAIN-SUFFIX,telegram.org,🌍 国倖网站",
12    "DOMAIN-SUFFIX,steampowered.com,🌍 国倖网站",
13    "DOMAIN-SUFFIX,openai.com,🌍 国倖网站",
14    "DOMAIN-SUFFIX,anthropic.com,🌍 国倖网站",
15    "DOMAIN-SUFFIX,claude.ai,🌍 国倖网站",
16    "DOMAIN-SUFFIX,cursor.com,🌍 国倖网站",
17    "DOMAIN-SUFFIX,cloudflare.com,🌍 国倖网站",
18    "DOMAIN-SUFFIX,netflix.com,🌍 国倖网站",
19    "DOMAIN-SUFFIX,spotify.com,🌍 国倖网站",
20    "DOMAIN-SUFFIX,disneyplus.com,🌍 国倖网站",
21    "DOMAIN-SUFFIX,cn,πŸ‡¨πŸ‡³ 国内网站",
22    "DOMAIN-SUFFIX,baidu.com,πŸ‡¨πŸ‡³ 国内网站",
23    "DOMAIN-SUFFIX,qq.com,πŸ‡¨πŸ‡³ 国内网站",
24    "DOMAIN-SUFFIX,weixin.com,πŸ‡¨πŸ‡³ 国内网站",
25    "DOMAIN-SUFFIX,alipay.com,πŸ‡¨πŸ‡³ 国内网站",
26    "GEOIP,CN,🎯 η›΄θΏž",
27    "MATCH,🌍 国倖网站",
28];
29
30const EXT_RULES: &[&str] = &[
31    "RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/BanEasyAD.list,πŸ›‘ εΉΏε‘Šζ‹¦ζˆͺ",
32    "RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/ProxyMedia.list,🌍 国倖网站",
33    "RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/ChinaDomain.list,πŸ‡¨πŸ‡³ 国内网站",
34    "GEOIP,CN,🎯 η›΄θΏž",
35    "MATCH,🌍 国倖网站",
36];
37
38pub fn build_config(nodes: &[ProxyNode], rule_mode: RuleMode) -> ClashConfig {
39    let names: Vec<String> = nodes.iter().map(|n| n.name.clone()).collect();
40    let mut foreign_proxies = vec![
41        "πŸš€ θŠ‚η‚Ήι€‰ζ‹©".to_string(),
42        "♻️ θ‡ͺεŠ¨ι€‰ζ‹©".to_string(),
43        "🎯 η›΄θΏž".to_string(),
44        "DIRECT".to_string(),
45    ];
46    foreign_proxies.extend(names.clone());
47
48    let rules = match rule_mode {
49        RuleMode::External => EXT_RULES.iter().map(|s| (*s).to_string()).collect(),
50        RuleMode::Builtin => BUILTIN_RULES.iter().map(|s| (*s).to_string()).collect(),
51    };
52
53    ClashConfig {
54        port: 7890,
55        socks_port: 7891,
56        allow_lan: true,
57        mode: "Rule".to_string(),
58        log_level: "info".to_string(),
59        external_controller: "127.0.0.1:9090".to_string(),
60        proxies: nodes.to_vec(),
61        proxy_groups: vec![
62            ProxyGroup {
63                name: "πŸš€ θŠ‚η‚Ήι€‰ζ‹©".to_string(),
64                group_type: "select".to_string(),
65                proxies: {
66                    let mut p = vec![
67                        "♻️ θ‡ͺεŠ¨ι€‰ζ‹©".to_string(),
68                        "🎯 η›΄θΏž".to_string(),
69                        "DIRECT".to_string(),
70                    ];
71                    p.extend(names.clone());
72                    p
73                },
74                url: None,
75                interval: None,
76            },
77            ProxyGroup {
78                name: "♻️ θ‡ͺεŠ¨ι€‰ζ‹©".to_string(),
79                group_type: "url-test".to_string(),
80                proxies: names.clone(),
81                url: Some("http://www.gstatic.com/generate_204".to_string()),
82                interval: Some(300),
83            },
84            ProxyGroup {
85                name: "🎯 η›΄θΏž".to_string(),
86                group_type: "select".to_string(),
87                proxies: {
88                    let mut p = vec!["DIRECT".to_string()];
89                    p.extend(names.clone());
90                    p
91                },
92                url: None,
93                interval: None,
94            },
95            ProxyGroup {
96                name: "πŸ›‘ εΉΏε‘Šζ‹¦ζˆͺ".to_string(),
97                group_type: "select".to_string(),
98                proxies: {
99                    let mut p = vec!["REJECT".to_string(), "DIRECT".to_string()];
100                    p.extend(names.clone());
101                    p
102                },
103                url: None,
104                interval: None,
105            },
106            ProxyGroup {
107                name: "🌍 国倖网站".to_string(),
108                group_type: "select".to_string(),
109                proxies: foreign_proxies,
110                url: None,
111                interval: None,
112            },
113            ProxyGroup {
114                name: "πŸ‡¨πŸ‡³ 国内网站".to_string(),
115                group_type: "select".to_string(),
116                proxies: {
117                    let mut p = vec!["🎯 η›΄θΏž".to_string()];
118                    p.extend(names);
119                    p
120                },
121                url: None,
122                interval: None,
123            },
124        ],
125        rules,
126    }
127}
128
129pub fn to_yaml(config: &ClashConfig) -> crate::Result<String> {
130    Ok(serde_yaml::to_string(config)?)
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::types::ProxyType;
137
138    fn sample_nodes() -> Vec<ProxyNode> {
139        vec![
140            ProxyNode::new("θŠ‚η‚Ή-A".into(), ProxyType::Ss, "1.2.3.4".into(), 443),
141            ProxyNode::new("θŠ‚η‚Ή-B".into(), ProxyType::Ss, "5.6.7.8".into(), 443),
142        ]
143    }
144
145    #[test]
146    fn foreign_group_follows_select() {
147        let config = build_config(&sample_nodes(), RuleMode::Builtin);
148        let foreign = config
149            .proxy_groups
150            .iter()
151            .find(|g| g.name == "🌍 国倖网站")
152            .unwrap();
153        assert_eq!(foreign.proxies[0], "πŸš€ θŠ‚η‚Ήι€‰ζ‹©");
154    }
155
156    #[test]
157    fn select_group_contains_all_nodes() {
158        let config = build_config(&sample_nodes(), RuleMode::Builtin);
159        let select = config
160            .proxy_groups
161            .iter()
162            .find(|g| g.name == "πŸš€ θŠ‚η‚Ήι€‰ζ‹©")
163            .unwrap();
164        assert!(select.proxies.contains(&"θŠ‚η‚Ή-A".to_string()));
165        assert!(select.proxies.contains(&"θŠ‚η‚Ή-B".to_string()));
166    }
167
168    #[test]
169    fn external_rules_use_rule_set() {
170        let config = build_config(&sample_nodes(), RuleMode::External);
171        assert!(config.rules[0].starts_with("RULE-SET"));
172    }
173}