use crate::types::{ClashConfig, ProxyGroup, ProxyNode, RuleMode};
const BUILTIN_RULES: &[&str] = &[
"DOMAIN-SUFFIX,ad.com,π εΉΏεζ¦ζͺ",
"DOMAIN-SUFFIX,doubleclick.net,π εΉΏεζ¦ζͺ",
"DOMAIN-SUFFIX,google.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,youtube.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,github.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,twitter.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,x.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,telegram.org,π ε½ε€η½η«",
"DOMAIN-SUFFIX,steampowered.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,openai.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,anthropic.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,claude.ai,π ε½ε€η½η«",
"DOMAIN-SUFFIX,cursor.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,cloudflare.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,netflix.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,spotify.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,disneyplus.com,π ε½ε€η½η«",
"DOMAIN-SUFFIX,cn,π¨π³ ε½ε
η½η«",
"DOMAIN-SUFFIX,baidu.com,π¨π³ ε½ε
η½η«",
"DOMAIN-SUFFIX,qq.com,π¨π³ ε½ε
η½η«",
"DOMAIN-SUFFIX,weixin.com,π¨π³ ε½ε
η½η«",
"DOMAIN-SUFFIX,alipay.com,π¨π³ ε½ε
η½η«",
"GEOIP,CN,π― η΄θΏ",
"MATCH,π ε½ε€η½η«",
];
const EXT_RULES: &[&str] = &[
"RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/BanEasyAD.list,π εΉΏεζ¦ζͺ",
"RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/ProxyMedia.list,π ε½ε€η½η«",
"RULE-SET,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/ChinaDomain.list,π¨π³ ε½ε
η½η«",
"GEOIP,CN,π― η΄θΏ",
"MATCH,π ε½ε€η½η«",
];
pub fn build_config(nodes: &[ProxyNode], rule_mode: RuleMode) -> ClashConfig {
let names: Vec<String> = nodes.iter().map(|n| n.name.clone()).collect();
let mut foreign_proxies = vec![
"π θηΉιζ©".to_string(),
"β»οΈ θͺε¨ιζ©".to_string(),
"π― η΄θΏ".to_string(),
"DIRECT".to_string(),
];
foreign_proxies.extend(names.clone());
let rules = match rule_mode {
RuleMode::External => EXT_RULES.iter().map(|s| (*s).to_string()).collect(),
RuleMode::Builtin => BUILTIN_RULES.iter().map(|s| (*s).to_string()).collect(),
};
ClashConfig {
port: 7890,
socks_port: 7891,
allow_lan: true,
mode: "Rule".to_string(),
log_level: "info".to_string(),
external_controller: "127.0.0.1:9090".to_string(),
proxies: nodes.to_vec(),
proxy_groups: vec![
ProxyGroup {
name: "π θηΉιζ©".to_string(),
group_type: "select".to_string(),
proxies: {
let mut p = vec![
"β»οΈ θͺε¨ιζ©".to_string(),
"π― η΄θΏ".to_string(),
"DIRECT".to_string(),
];
p.extend(names.clone());
p
},
url: None,
interval: None,
},
ProxyGroup {
name: "β»οΈ θͺε¨ιζ©".to_string(),
group_type: "url-test".to_string(),
proxies: names.clone(),
url: Some("http://www.gstatic.com/generate_204".to_string()),
interval: Some(300),
},
ProxyGroup {
name: "π― η΄θΏ".to_string(),
group_type: "select".to_string(),
proxies: {
let mut p = vec!["DIRECT".to_string()];
p.extend(names.clone());
p
},
url: None,
interval: None,
},
ProxyGroup {
name: "π εΉΏεζ¦ζͺ".to_string(),
group_type: "select".to_string(),
proxies: {
let mut p = vec!["REJECT".to_string(), "DIRECT".to_string()];
p.extend(names.clone());
p
},
url: None,
interval: None,
},
ProxyGroup {
name: "π ε½ε€η½η«".to_string(),
group_type: "select".to_string(),
proxies: foreign_proxies,
url: None,
interval: None,
},
ProxyGroup {
name: "π¨π³ ε½ε
η½η«".to_string(),
group_type: "select".to_string(),
proxies: {
let mut p = vec!["π― η΄θΏ".to_string()];
p.extend(names);
p
},
url: None,
interval: None,
},
],
rules,
}
}
pub fn to_yaml(config: &ClashConfig) -> crate::Result<String> {
Ok(serde_yaml::to_string(config)?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::ProxyType;
fn sample_nodes() -> Vec<ProxyNode> {
vec![
ProxyNode::new("θηΉ-A".into(), ProxyType::Ss, "1.2.3.4".into(), 443),
ProxyNode::new("θηΉ-B".into(), ProxyType::Ss, "5.6.7.8".into(), 443),
]
}
#[test]
fn foreign_group_follows_select() {
let config = build_config(&sample_nodes(), RuleMode::Builtin);
let foreign = config
.proxy_groups
.iter()
.find(|g| g.name == "π ε½ε€η½η«")
.unwrap();
assert_eq!(foreign.proxies[0], "π θηΉιζ©");
}
#[test]
fn select_group_contains_all_nodes() {
let config = build_config(&sample_nodes(), RuleMode::Builtin);
let select = config
.proxy_groups
.iter()
.find(|g| g.name == "π θηΉιζ©")
.unwrap();
assert!(select.proxies.contains(&"θηΉ-A".to_string()));
assert!(select.proxies.contains(&"θηΉ-B".to_string()));
}
#[test]
fn external_rules_use_rule_set() {
let config = build_config(&sample_nodes(), RuleMode::External);
assert!(config.rules[0].starts_with("RULE-SET"));
}
}