├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Test ├── demo01.rs └── demo02.rs ├── TestData ├── 0.csv ├── 1.csv ├── 2.csv ├── 3.csv └── 4.txt ├── config.yaml ├── data ├── CloudflareST.exe ├── ipv4-104.txt ├── ipv4.txt ├── result.csv ├── 使用CloudflareST测试延迟.bat └── 相关程序的备份 │ ├── 1_CloudflareST │ ├── CloudflareST.exe │ ├── ipv4.txt │ ├── ipv6.txt │ └── 使用CloudflareST测试延迟.bat │ ├── 2_ipspeedtest │ ├── ip.csv │ ├── ip.txt │ ├── ipspeedtest.exe │ ├── locations.json │ └── 使用ipspeedtest测试延迟和速度.bat │ └── 命令示例.txt ├── images └── 图1.png ├── src ├── main.rs └── utils │ ├── build.rs │ ├── clash.rs │ ├── config.rs │ ├── convert.rs │ ├── file_data.rs │ ├── mod.rs │ ├── net_data.rs │ ├── qrcode.rs │ ├── singbox.rs │ └── v2ray.rs ├── template ├── clash.yaml └── sing-box.json └── 使用说明.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-cfwks-subconverter-yaml" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | clap = { version = "4.5", features = ["derive"] } 8 | actix-web = "4" 9 | serde = "1.0" 10 | serde_yaml = "0.9" 11 | serde_json = "1.0" 12 | regex = "1" 13 | encoding = "0.2" 14 | serde_urlencoded = "0.7.1" 15 | rand = "0.8.5" 16 | urlencoding = "2.1.3" 17 | serde_qs = "0.13.0" 18 | lazy_static = "1.5.0" 19 | local-ip-address = "0.6.1" 20 | qrcode = "0.14.1" 21 | image = "0.25.2" 22 | base64 = "0.13.1" 23 | csv = "1.3.1" 24 | tokio = { version = "1", features = ["full"] } 25 | reqwest = { version = "0.12", features = ["json"] } 26 | crossbeam-channel = "0.5" 27 | 28 | 29 | # [[bin]] 30 | # name = "app" 31 | # path = "Test/demo02.rs" 32 | -------------------------------------------------------------------------------- /Test/demo01.rs: -------------------------------------------------------------------------------- 1 | use csv::ReaderBuilder; 2 | use std::{ 3 | collections::HashMap, 4 | error::Error, 5 | fs::{ self, File }, 6 | io::{ BufRead, BufReader }, 7 | path::Path, 8 | vec, 9 | }; 10 | use regex::Regex; 11 | use lazy_static::lazy_static; 12 | 13 | #[derive(Default, Clone)] 14 | pub struct MyData { 15 | pub addr: String, 16 | pub port: Option, 17 | pub alias: Option, 18 | } 19 | 20 | #[derive(Default)] 21 | struct FileData { 22 | addr: String, // IP地址或者域名地址 23 | port: Option, 24 | colo: Option, // 数据中心(3位字母) 25 | loc: Option, // 国家代码/地区代码(2位字母) 26 | region: Option, // 地区 27 | city: Option, 28 | } 29 | 30 | lazy_static! { 31 | // 匹配一个或多个空白字符 32 | static ref SPACE_REGEX: Regex = Regex::new(r"\s+").unwrap(); 33 | // 匹配"IPv4 PORT"(可以1个以上的空格) 34 | static ref IPV4_PORT_SPACE_REGEX: Regex = Regex::new(r"^\s*([0-9.]+)\s+(\d+)\s*$").unwrap(); 35 | // 匹配"IPv6 PORT"(可以1个以上的空格) 36 | static ref IPV6_PORT_SPACE_REGEX: Regex = Regex::new( 37 | r"^\s*((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))\s*" 38 | ).unwrap(); 39 | // 匹配"[IPv6]:PORT" 40 | static ref IPV6_PORT_BRACKET_REGEX: Regex = Regex::new( 41 | r"^\s*\[([0-9a-fA-F:.]+)\]:(\d+)\s*$" 42 | ).unwrap(); 43 | // 匹配"IPv6,PORT"(逗号左右可以零个以上的空格) 44 | static ref IPV6_PORT_COMMA_REGEX: Regex = Regex::new( 45 | r"([0-9a-fA-F:]+:[0-9a-fA-F:]+)\s*,\s*(\d+)" 46 | ).unwrap(); 47 | } 48 | 49 | fn process_csv(filename: &str, default_port: u16) -> Result, Box> { 50 | let file = File::open(filename)?; 51 | let mut rdr = ReaderBuilder::new().from_reader(file); 52 | 53 | // 读取文件头 54 | let headers = rdr.headers()?; 55 | 56 | // 可能的字段映射关系,以列名作为键,其它别名的列名作为值(向量) 57 | let mut field_map: HashMap<&str, Vec<&str>> = HashMap::new(); 58 | field_map.insert("addr", vec!["IP", "IP地址", "IP 地址", "网络地址"]); 59 | field_map.insert("port", vec!["PORT", "端口"]); 60 | field_map.insert("colo", vec!["colo", "iata", "数据中心"]); 61 | field_map.insert("loc", vec!["cca2", "alpha-2", "Country Code", "CountryCode", "国家代码"]); 62 | field_map.insert("region", vec!["region", "区域", "地区"]); 63 | field_map.insert("city", vec!["city", "城市"]); 64 | 65 | // 尝试从标题中查找列索引(下标) 66 | let find_index = |key: &str| { 67 | field_map.get(key).and_then(|candidates| 68 | candidates.iter().find_map(|&field| 69 | headers.iter().position( 70 | |header| header.trim().to_lowercase() == field.trim().to_lowercase() // 忽略字段中的大小写 71 | ) 72 | ) 73 | ) 74 | }; 75 | // 找csv标题的列名跟向量中哪个元素对应 => 在哪个索引(下标)中 76 | let addr_index = find_index("addr"); 77 | let port_index = find_index("port"); 78 | let colo_index = find_index("colo"); 79 | let loc_index = find_index("loc"); 80 | let region_index = find_index("region"); 81 | let city_index = find_index("city"); 82 | 83 | let mut result: Vec = Vec::new(); 84 | 85 | for record in rdr.records() { 86 | let record = record?; 87 | 88 | // 获取`IP地址`字段的值 89 | let addr_column = addr_index.and_then(|index| record.get(index)).unwrap_or(""); 90 | 91 | if addr_column.is_empty() { 92 | continue; 93 | } 94 | 95 | // 获取`端口`字段的值 96 | let port_column: u16 = port_index 97 | .and_then(|index| record.get(index).and_then(|val| val.parse::().ok())) // 显示转换 98 | .unwrap_or(default_port); // 默认为`default_port` 99 | 100 | // 定义一个闭包来处理列的提取逻辑(只支持String数据类型的数据提取) 101 | let get_column_string = |index: Option| { 102 | index 103 | .and_then(|idx| record.get(idx).and_then(|val| val.parse().ok())) // 隐式转换 104 | .unwrap_or_else(|| "".to_string()) // 默认为空字符串 105 | }; 106 | 107 | // 使用闭包提取列数据,没有找到对应的列时,返回空字符串 108 | let colo_column = get_column_string(colo_index); 109 | let loc_column = get_column_string(loc_index); 110 | let region_column = get_column_string(region_index); 111 | let city_column = get_column_string(city_index); 112 | 113 | let data = FileData { 114 | addr: addr_column.to_string(), 115 | port: Some(port_column), 116 | colo: Some(colo_column), 117 | loc: Some(loc_column), 118 | region: Some(region_column), 119 | city: Some(city_column), 120 | }; 121 | result.push(data); 122 | } 123 | 124 | Ok(result) 125 | } 126 | 127 | fn process_txt(filename: &str, default_port: u16) -> Result, Box> { 128 | // 排除不需要的txt文件 129 | if filename.starts_with("ips-v") || filename.starts_with("ipv") { 130 | return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Skipping this file"))); 131 | } 132 | let file = File::open(filename)?; 133 | let reader = BufReader::new(file); 134 | 135 | let mut seen_lines: Vec = Vec::new(); 136 | let mut result: Vec = Vec::new(); 137 | 138 | for line in reader.lines() { 139 | let line = line?; 140 | let trimmed_line = line.trim().to_string(); 141 | 142 | if 143 | trimmed_line.is_empty() || 144 | trimmed_line.contains("/") || // 跳过包含"/"的行 => CIDR 145 | seen_lines.contains(&trimmed_line) 146 | { 147 | continue; 148 | } 149 | 150 | // 提取地址和端口 151 | let parts: Vec = if 152 | let Some(captures) = IPV6_PORT_COMMA_REGEX.captures(&trimmed_line) 153 | { 154 | // 判断是否为 "IPv6, PORT" 格式(逗号左右,可以0个以上的空格) 155 | let ipv6 = captures.get(1).map_or("", |m| m.as_str()); 156 | let port = captures.get(2).map_or("", |m| m.as_str()); 157 | vec![format!("[{}]", ipv6), port.to_string()] 158 | } else if IPV6_PORT_SPACE_REGEX.is_match(&trimmed_line) { 159 | // 判断是否为 "IPv6 PORT" 地址 160 | SPACE_REGEX.splitn(&trimmed_line, 2) 161 | .map(|s| { 162 | let str_s = s.to_string(); 163 | let colon_count = str_s 164 | .chars() 165 | .filter(|&c| c == ':') 166 | .count(); 167 | if colon_count > 1 { 168 | if str_s.starts_with('[') && str_s.ends_with(']') { 169 | str_s // 已经有方括号,直接返回 170 | } else { 171 | format!("[{}]", str_s) // 添加方括号 172 | } 173 | } else { 174 | str_s // 不满足条件,直接返回 175 | } 176 | }) 177 | .collect() 178 | } else if let Some(captures) = IPV6_PORT_BRACKET_REGEX.captures(&trimmed_line) { 179 | // 判断是否为 "[IPv6]:PORT" 格式 180 | vec![ 181 | format!("[{}]", captures.get(1).unwrap().as_str().to_string()), 182 | captures.get(2).unwrap().as_str().to_string() 183 | ] 184 | } else if let Some(captures) = IPV4_PORT_SPACE_REGEX.captures(&trimmed_line) { 185 | // 判断是否为 "IPv4 PORT" 格式 186 | vec![ 187 | captures.get(1).unwrap().as_str().to_string(), 188 | captures.get(2).unwrap().as_str().to_string() 189 | ] 190 | } else if 191 | trimmed_line.contains(':') && 192 | trimmed_line 193 | .chars() 194 | .filter(|&c| c == ':') 195 | .count() == 1 196 | { 197 | // 判断是否为 "IPv4:PORT" 或 "Domain:PORT" 格式 198 | trimmed_line 199 | .splitn(2, ':') 200 | .map(|s| s.to_string()) 201 | .collect() 202 | } else if trimmed_line.contains(", ") { 203 | // 判断是否为 "IPv4, PORT" 、"[IPv6], PORT"、" "Domain, PORT" 格式 204 | trimmed_line 205 | .splitn(2, ", ") 206 | .map(|s| s.to_string()) 207 | .collect() 208 | } else if trimmed_line.contains(',') { 209 | // 判断是否为 "IPv4,PORT" 、"[IPv6],PORT"、" "Domain,PORT" 格式 210 | trimmed_line 211 | .splitn(2, ',') 212 | .map(|s| s.to_string()) 213 | .collect() 214 | } else if SPACE_REGEX.is_match(&trimmed_line) { 215 | // 判断是否为 "[IPv6] PORT" 或 "Domain PORT" 格式 216 | let value = SPACE_REGEX.splitn(&trimmed_line, 2) 217 | .map(|s| s.to_string()) 218 | .collect(); 219 | value 220 | } else { 221 | // 匹配 "IPv4"、"[ipv6]"、"Domain" 格式 222 | vec![trimmed_line.to_string(), default_port.to_string()] 223 | }; 224 | 225 | if parts.len() == 2 { 226 | let final_line = format!("{}:{}", parts[0], parts[1]); 227 | if !seen_lines.contains(&final_line) { 228 | let cdn_address = FileData { 229 | addr: parts[0].clone(), 230 | port: parts[1].parse::().ok(), 231 | ..Default::default() // 其它字段不管,使用默认值 232 | }; 233 | seen_lines.push(final_line); 234 | result.push(cdn_address); 235 | } 236 | } else { 237 | println!("不支持提取 `{}` 的地址和端口!", trimmed_line); 238 | } 239 | } 240 | 241 | Ok(result) 242 | } 243 | 244 | fn process_file(filename: &str, default_port: u16) -> Result, Box> { 245 | let path = Path::new(filename); 246 | let extension = path.extension().and_then(|s| s.to_str()); 247 | 248 | match extension { 249 | Some("csv") => process_csv(filename, default_port), 250 | Some("txt") => process_txt(filename, default_port), 251 | _ => Err("不支持的文件类型".into()), 252 | } 253 | } 254 | 255 | pub fn read_files_data( 256 | field_column: &str, 257 | default_port: u16, 258 | count: usize, 259 | folder_path: &str 260 | ) -> Vec { 261 | let mut seen_addr: Vec = Vec::new(); // 多文件数据去重 262 | let mut results: Vec = Vec::new(); // 存储结果 263 | 264 | let entries = fs::read_dir(folder_path).expect("无法读取目录"); 265 | 'outer: for entry in entries { 266 | let entry = entry.unwrap(); 267 | let path = entry.path(); 268 | // 只处理txt和csv文件,process_file函数中,含有过滤掉不要读取的txt文件 269 | if path.is_file() && path.extension().map_or(false, |ext| (ext == "txt" || ext == "csv")) { 270 | let file_name = path.file_name().unwrap().to_string_lossy(); 271 | let filename: String = format!("{}/{}", folder_path, file_name); 272 | match process_file(&filename, default_port) { 273 | Ok(data) => { 274 | for item in &data { 275 | let addr: String = item.addr.clone(); 276 | let port: u16 = item.port.unwrap_or(default_port); 277 | let addr_port = format!("{}:{}", addr, port); 278 | 279 | // 确保数据的唯一性(如果读取多文件,可能不同的文件,拥有相同的数据) 280 | if seen_addr.contains(&addr_port) { 281 | continue; 282 | } else { 283 | seen_addr.push(addr_port.clone()); 284 | } 285 | 286 | // 获取某个字段值作为节点的别名前缀使用(比如:colo => SJC, loc => US 等) 287 | // 注意,字段值可能是空值,后续需要再次判断 288 | let alias_prefix = match field_column { 289 | "colo" => item.colo.clone(), // 数据中心(3个字母) 290 | "loc" => item.loc.clone(), // 国家代码(2个字母) 291 | "region" => item.region.clone(), // 地区 292 | "city" => item.city.clone(), // 城市 293 | _ => Some("".to_string()), 294 | }; 295 | 296 | // (选择性)将需要的字段值,以MyData结构体形式存储 297 | let data = MyData { 298 | addr: addr.clone(), 299 | port: Some(port), 300 | alias: alias_prefix, 301 | }; 302 | 303 | // 获取足够的数据,就停止for循环 304 | if results.len() < count { 305 | results.push(data.clone()); 306 | } else { 307 | break 'outer; // 跳出标记为 'outer 的外层循环 308 | } 309 | } 310 | } 311 | Err(e) => eprintln!("处理文件 `{}` 出错: {}", filename, e), 312 | } 313 | } 314 | } 315 | 316 | results 317 | } 318 | 319 | fn main() { 320 | let folder_path = "./TestData"; 321 | let count = 200; // 获取前n个数据 322 | let default_port: u16 = 443; 323 | let field_column = "loc"; // 没有找到相关的字段,就以空字符串替代 324 | let results = read_files_data(field_column, default_port, count, folder_path); 325 | println!("获取到数据:{}", results.len()); 326 | for item in &results { 327 | println!( 328 | "{}|{}:{}", 329 | item.alias.as_ref().unwrap_or(&"".to_string()), 330 | item.addr, 331 | item.port.unwrap_or(default_port) 332 | ); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /Test/demo02.rs: -------------------------------------------------------------------------------- 1 | use csv::ReaderBuilder; 2 | use reqwest; 3 | use std::{ collections::HashMap, error::Error, thread }; 4 | use crossbeam_channel::unbounded; // 需要引入 crossbeam-channel 库 5 | use regex::Regex; 6 | use lazy_static::lazy_static; 7 | 8 | lazy_static! { 9 | // 匹配一个或多个空白字符 10 | static ref SPACE_REGEX: Regex = Regex::new(r"\s+").unwrap(); 11 | // 匹配"IPv4 PORT"(可以1个以上的空格) 12 | static ref IPV4_PORT_SPACE_REGEX: Regex = Regex::new(r"^\s*([0-9.]+)\s+(\d+)\s*$").unwrap(); 13 | // 匹配"IPv6 PORT"(可以1个以上的空格) 14 | static ref IPV6_PORT_SPACE_REGEX: Regex = Regex::new( 15 | r"^\s*((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))\s*" 16 | ).unwrap(); 17 | // 匹配"[IPv6]:PORT" 18 | static ref IPV6_PORT_BRACKET_REGEX: Regex = Regex::new( 19 | r"^\s*\[([0-9a-fA-F:.]+)\]:(\d+)\s*$" 20 | ).unwrap(); 21 | // 匹配"IPv6,PORT"(逗号左右可以零个以上的空格) 22 | static ref IPV6_PORT_COMMA_REGEX: Regex = Regex::new( 23 | r"([0-9a-fA-F:]+:[0-9a-fA-F:]+)\s*,\s*(\d+)" 24 | ).unwrap(); 25 | } 26 | 27 | // 1. 定义数据结构 (假设你的 CSV 有两列:name 和 age) 28 | #[derive(Default)] 29 | pub struct FileData { 30 | pub addr: String, // IP地址或者域名地址 31 | pub port: Option, 32 | pub colo: Option, // 数据中心(3位字母) 33 | pub loc: Option, // 国家代码/地区代码(2位字母) 34 | pub region: Option, // 地区 35 | pub city: Option, 36 | } 37 | 38 | #[derive(Default, Clone)] 39 | pub struct MyData { 40 | pub addr: String, 41 | pub port: Option, 42 | pub alias: Option, 43 | } 44 | 45 | // 同步函数中使用异步:使用 std::thread::spawn 在另一个线程中运行异步代码 46 | fn read_csv_from_url( 47 | url: &str, 48 | default_port: u16 49 | ) -> Result, Box> { 50 | let (sender, receiver) = unbounded(); 51 | let url_copy = url.to_string(); 52 | 53 | thread::spawn(move || { 54 | let rt = tokio::runtime::Runtime::new().unwrap(); 55 | let result = rt.block_on(async { 56 | let response = reqwest::get(&url_copy).await?; 57 | if !response.status().is_success() { 58 | return Err(format!("Failed to fetch CSV from URL: {}", response.status()).into()); 59 | } 60 | let body = response.text().await?; 61 | let mut rdr = ReaderBuilder::new().from_reader(body.as_bytes()); 62 | let headers = rdr.headers()?; // 读取并忽略头部 63 | 64 | // 可能的字段映射关系,以列名作为键,其它别名的列名作为值(向量) 65 | let mut field_map: HashMap<&str, Vec<&str>> = HashMap::new(); 66 | field_map.insert("addr", vec!["IP", "IP地址", "IP 地址", "网络地址"]); 67 | field_map.insert("port", vec!["PORT", "端口"]); 68 | field_map.insert("colo", vec!["colo", "iata", "数据中心"]); 69 | field_map.insert( 70 | "loc", 71 | vec!["cca2", "alpha-2", "Country Code", "CountryCode", "国家代码"] 72 | ); 73 | field_map.insert("region", vec!["region", "区域", "地区"]); 74 | field_map.insert("city", vec!["city", "城市"]); 75 | 76 | // 尝试从标题中查找列索引(下标) 77 | let find_index = |key: &str| { 78 | field_map.get(key).and_then(|candidates| 79 | candidates.iter().find_map(|&field| 80 | headers.iter().position( 81 | |header| header.trim().to_lowercase() == field.trim().to_lowercase() // 忽略字段中的大小写 82 | ) 83 | ) 84 | ) 85 | }; 86 | // 找csv标题的列名跟向量中哪个元素对应 => 在哪个索引(下标)中 87 | let addr_index = find_index("addr"); 88 | let port_index = find_index("port"); 89 | let colo_index = find_index("colo"); 90 | let loc_index = find_index("loc"); 91 | let region_index = find_index("region"); 92 | let city_index = find_index("city"); 93 | 94 | // 2. 修改异步代码,将 CSV 记录转换为结构体实例,并收集到向量 95 | let mut records: Vec = Vec::new(); 96 | 97 | for record in rdr.records() { 98 | let record = record?; 99 | 100 | // 获取`IP地址`字段的值 101 | let addr_column = addr_index.and_then(|index| record.get(index)).unwrap_or(""); 102 | 103 | if addr_column.is_empty() { 104 | continue; 105 | } 106 | 107 | // 获取`端口`字段的值 108 | let port_column: u16 = port_index 109 | .and_then(|index| record.get(index).and_then(|val| val.parse::().ok())) // 显示转换 110 | .unwrap_or(default_port); // 默认为`default_port` 111 | 112 | // 定义一个闭包来处理列的提取逻辑(只支持String数据类型的数据提取) 113 | let get_column_string = |index: Option| { 114 | index 115 | .and_then(|idx| record.get(idx).and_then(|val| val.parse().ok())) // 隐式转换 116 | .unwrap_or_else(|| "".to_string()) // 默认为空字符串 117 | }; 118 | 119 | // 使用闭包提取列数据,没有找到对应的列时,返回空字符串 120 | let colo_column = get_column_string(colo_index); 121 | let loc_column = get_column_string(loc_index); 122 | let region_column = get_column_string(region_index); 123 | let city_column = get_column_string(city_index); 124 | 125 | let data = FileData { 126 | addr: addr_column.to_string(), 127 | port: Some(port_column), 128 | colo: Some(colo_column), 129 | loc: Some(loc_column), 130 | region: Some(region_column), 131 | city: Some(city_column), 132 | }; 133 | records.push(data); 134 | } 135 | Ok(records) // 返回向量 136 | }); 137 | sender.send(result).unwrap(); 138 | }); 139 | 140 | // 4. 通过通道发送结果 141 | receiver.recv().unwrap() // 接收通道中的向量 142 | } 143 | 144 | fn read_txt_from_url( 145 | url: &str, 146 | default_port: u16 147 | ) -> Result, Box> { 148 | let (sender, receiver) = unbounded(); 149 | let url_copy = url.to_string(); 150 | let mut seen_lines: Vec = Vec::new(); 151 | thread::spawn(move || { 152 | let rt = tokio::runtime::Runtime::new().unwrap(); 153 | let result = rt.block_on(async { 154 | let response = reqwest::get(&url_copy).await?; 155 | if !response.status().is_success() { 156 | return Err(format!("Failed to fetch CSV from URL: {}", response.status()).into()); 157 | } 158 | let body = response.text().await?; 159 | let mut records: Vec = Vec::new(); 160 | for line in body.lines() { 161 | let trimmed_line = line.trim().to_string(); 162 | 163 | if 164 | trimmed_line.is_empty() || 165 | trimmed_line.contains("/") || // 跳过包含"/"的行 => CIDR 166 | seen_lines.contains(&trimmed_line) 167 | { 168 | continue; 169 | } 170 | 171 | // 提取地址和端口 172 | let parts: Vec = if 173 | let Some(captures) = IPV6_PORT_COMMA_REGEX.captures(&trimmed_line) 174 | { 175 | // 判断是否为 "IPv6, PORT" 格式(逗号左右,可以0个以上的空格) 176 | let ipv6 = captures.get(1).map_or("", |m| m.as_str()); 177 | let port = captures.get(2).map_or("", |m| m.as_str()); 178 | vec![format!("[{}]", ipv6), port.to_string()] 179 | } else if IPV6_PORT_SPACE_REGEX.is_match(&trimmed_line) { 180 | // 判断是否为 "IPv6 PORT" 地址 181 | SPACE_REGEX.splitn(&trimmed_line, 2) 182 | .map(|s| { 183 | let str_s = s.to_string(); 184 | let colon_count = str_s 185 | .chars() 186 | .filter(|&c| c == ':') 187 | .count(); 188 | if colon_count > 1 { 189 | if str_s.starts_with('[') && str_s.ends_with(']') { 190 | str_s // 已经有方括号,直接返回 191 | } else { 192 | format!("[{}]", str_s) // 添加方括号 193 | } 194 | } else { 195 | str_s // 不满足条件,直接返回 196 | } 197 | }) 198 | .collect() 199 | } else if let Some(captures) = IPV6_PORT_BRACKET_REGEX.captures(&trimmed_line) { 200 | // 判断是否为 "[IPv6]:PORT" 格式 201 | vec![ 202 | format!("[{}]", captures.get(1).unwrap().as_str().to_string()), 203 | captures.get(2).unwrap().as_str().to_string() 204 | ] 205 | } else if let Some(captures) = IPV4_PORT_SPACE_REGEX.captures(&trimmed_line) { 206 | // 判断是否为 "IPv4 PORT" 格式 207 | vec![ 208 | captures.get(1).unwrap().as_str().to_string(), 209 | captures.get(2).unwrap().as_str().to_string() 210 | ] 211 | } else if 212 | trimmed_line.contains(':') && 213 | trimmed_line 214 | .chars() 215 | .filter(|&c| c == ':') 216 | .count() == 1 217 | { 218 | // 判断是否为 "IPv4:PORT" 或 "Domain:PORT" 格式 219 | trimmed_line 220 | .splitn(2, ':') 221 | .map(|s| s.to_string()) 222 | .collect() 223 | } else if trimmed_line.contains(", ") { 224 | // 判断是否为 "IPv4, PORT" 、"[IPv6], PORT"、" "Domain, PORT" 格式 225 | trimmed_line 226 | .splitn(2, ", ") 227 | .map(|s| s.to_string()) 228 | .collect() 229 | } else if trimmed_line.contains(',') { 230 | // 判断是否为 "IPv4,PORT" 、"[IPv6],PORT"、" "Domain,PORT" 格式 231 | trimmed_line 232 | .splitn(2, ',') 233 | .map(|s| s.to_string()) 234 | .collect() 235 | } else if SPACE_REGEX.is_match(&trimmed_line) { 236 | // 判断是否为 "[IPv6] PORT" 或 "Domain PORT" 格式 237 | let value = SPACE_REGEX.splitn(&trimmed_line, 2) 238 | .map(|s| s.to_string()) 239 | .collect(); 240 | value 241 | } else { 242 | // 匹配 "IPv4"、"[ipv6]"、"Domain" 格式 243 | vec![trimmed_line.to_string(), default_port.to_string()] 244 | }; 245 | 246 | if parts.len() == 2 { 247 | let final_line = format!("{}:{}", parts[0], parts[1]); 248 | if !seen_lines.contains(&final_line) { 249 | let data = FileData { 250 | addr: parts[0].clone(), 251 | port: parts[1].parse::().ok(), 252 | ..Default::default() // 其它字段不管,使用默认值 253 | }; 254 | seen_lines.push(final_line); 255 | records.push(data); 256 | } 257 | } else { 258 | println!("不支持提取 `{}` 的地址和端口!", trimmed_line); 259 | } 260 | } 261 | Ok(records) 262 | }); 263 | sender.send(result).unwrap(); 264 | }); 265 | 266 | receiver.recv().unwrap() 267 | } 268 | 269 | fn process_network_data( 270 | field_column: &str, 271 | default_port: u16, 272 | count: usize, 273 | url: &str 274 | ) -> Vec { 275 | let mut results: Vec = Vec::new(); // 存储结果 276 | let mut seen_addr = Vec::new(); 277 | 278 | if url.to_lowercase().starts_with("https://") && url.to_lowercase().ends_with(".csv") { 279 | match read_csv_from_url(url, default_port) { 280 | Ok(data) => { 281 | for item in &data { 282 | let addr: String = item.addr.clone(); 283 | let port: u16 = item.port.unwrap_or(default_port); 284 | let addr_port = format!("{}:{}", addr, port); 285 | 286 | // 数据去重,确保获取到数据没有重复的 287 | if seen_addr.contains(&addr_port) { 288 | continue; 289 | } else { 290 | seen_addr.push(addr_port.clone()); 291 | } 292 | 293 | // 获取某个字段值作为节点的别名前缀使用,注意,找不到对应的字段,则默认为空值,后面需要做处理 294 | let alias_prefix = match field_column { 295 | "colo" => item.colo.clone(), 296 | "loc" => item.loc.clone(), 297 | "region" => item.region.clone(), 298 | "city" => item.city.clone(), 299 | _ => Some("".to_string()), 300 | }; 301 | 302 | // (选择性)将需要的字段值,以MyData结构体形式存储 303 | let data = MyData { 304 | addr: addr.clone(), 305 | port: Some(port), 306 | alias: alias_prefix, 307 | }; 308 | 309 | // 如果结果数量小于指定的数量,则添加数据,否则就返回,避免无意义的IO操作(读取数据) 310 | if results.len() < count { 311 | results.push(data.clone()); 312 | } else { 313 | break; 314 | } 315 | } 316 | } 317 | Err(e) => eprintln!("Error: {}", e), 318 | } 319 | } else if url.to_lowercase().starts_with("https://") && url.to_lowercase().ends_with(".txt") { 320 | match read_txt_from_url(url, default_port) { 321 | Ok(data) => { 322 | for item in &data { 323 | let addr: String = item.addr.clone(); 324 | let port: u16 = item.port.unwrap_or(default_port); 325 | let addr_port = format!("{}:{}", addr, port); 326 | 327 | // 数据去重,确保获取到数据没有重复的 328 | if seen_addr.contains(&addr_port) { 329 | continue; 330 | } else { 331 | seen_addr.push(addr_port.clone()); 332 | } 333 | 334 | // 获取某个字段值作为节点的别名前缀使用,注意,找不到对应的字段,则默认为空值,后面需要做处理 335 | let alias_prefix = match field_column { 336 | "colo" => item.colo.clone(), 337 | "loc" => item.loc.clone(), 338 | "region" => item.region.clone(), 339 | "city" => item.city.clone(), 340 | _ => Some("".to_string()), 341 | }; 342 | 343 | // (选择性)将需要的字段值,以MyData结构体形式存储 344 | let data = MyData { 345 | addr: addr.clone(), 346 | port: Some(port), 347 | alias: alias_prefix, 348 | }; 349 | 350 | // 如果结果数量小于指定的数量,则添加数据,否则就返回,避免无意义的IO操作(读取数据) 351 | if results.len() < count { 352 | results.push(data.clone()); 353 | } else { 354 | break; 355 | } 356 | } 357 | } 358 | Err(e) => eprintln!("Error: {}", e), 359 | } 360 | } 361 | 362 | results 363 | } 364 | 365 | fn main() { 366 | let count = 100; 367 | let default_port = 443; 368 | let field_column = "colo"; 369 | let url = 370 | "https://raw.githubusercontent.com/juerson/actix-cfwks-subconverter-yaml/refs/heads/master/data/result.csv"; 371 | // let url = 372 | // "https://raw.githubusercontent.com/juerson/wireguard_converted_nekoray/refs/heads/main/ip.txt"; 373 | 374 | let my_data = process_network_data(field_column, default_port, count, url); 375 | 376 | println!("{:?}", my_data.len()); 377 | 378 | for item in my_data { 379 | let addr = item.addr.to_string(); 380 | let port = item.port.unwrap_or(default_port); 381 | let alias = item.alias.unwrap_or("".to_string()); 382 | println!("addr: {}, port: {} alias: {}", addr, port, alias); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /TestData/0.csv: -------------------------------------------------------------------------------- 1 | IP地址,端口,回源端口,TLS,数据中心,地区,国家,城市,TCP延迟(ms),速度(MB/s) 2 | 34.69.150.180,443,443,true,ORD,North America,US,Chicago,231,8.63 3 | 34.69.150.180,443,443,true,ORD,North America,US,Chicago,371,8.60 4 | 34.55.194.208,443,443,true,ORD,North America,US,Chicago,383,8.55 5 | 35.202.158.120,443,443,true,ORD,North America,US,Chicago,213,8.53 6 | 34.70.91.86,443,443,true,ORD,North America,US,Chicago,315,8.46 7 | 35.228.132.81,8443,443,true,ARN,Europe,SE,Stockholm,254,8.31 8 | 34.22.142.117,443,443,true,CDG,Europe,FR,Paris,321,8.29 9 | 35.228.132.81,2053,443,true,ARN,Europe,SE,Stockholm,273,8.19 10 | 35.196.72.166,443,443,true,ATL,North America,US,Atlanta,613,8.17 11 | 35.190.152.145,443,443,true,ATL,North America,US,Atlanta,333,7.90 12 | 34.22.142.117,443,443,true,CDG,Europe,FR,Paris,412,7.50 13 | 35.204.231.100,443,443,true,AMS,Europe,NL,Amsterdam,409,7.20 14 | 34.175.202.195,443,443,true,MAD,Europe,ES,Madrid,274,6.47 15 | 35.228.36.82,443,443,true,ARN,Europe,SE,Stockholm,325,6.34 16 | 34.105.233.161,443,443,true,LHR,Europe,GB,London,273,6.01 17 | 35.189.99.124,443,443,true,LHR,Europe,GB,London,286,5.92 18 | 34.147.196.2,443,443,true,LHR,Europe,GB,London,240,5.42 19 | 35.197.239.137,443,443,true,LHR,Europe,GB,London,360,5.25 20 | 34.105.189.25,443,443,true,LHR,Europe,GB,London,300,5.06 21 | 34.105.140.105,443,443,true,LHR,Europe,GB,London,244,3.83 22 | 34.175.217.93,80,443,false,MAD,Europe,ES,Madrid,250,2.28 23 | 35.200.238.235,443,443,true,BOM,Asia Pacific,IN,Mumbai,460,0.44 24 | 35.200.238.235,443,443,true,BOM,Asia Pacific,IN,Mumbai,509,0.24 25 | 35.200.238.235,443,443,true,BOM,Asia Pacific,IN,Mumbai,196,0.22 26 | 35.201.107.185,80,80,false,SIN,Asia Pacific,SG,Singapore,57,0.00 27 | 34.93.79.152,80,80,false,BOM,Asia Pacific,IN,Mumbai,62,0.00 28 | 34.93.226.165,80,80,false,BOM,Asia Pacific,IN,Mumbai,77,0.00 29 | 34.111.135.19,80,80,false,SIN,Asia Pacific,SG,Singapore,80,0.00 30 | 34.93.79.152,80,80,false,BOM,Asia Pacific,IN,Mumbai,82,0.00 31 | 35.200.202.118,80,80,false,BOM,Asia Pacific,IN,Mumbai,113,0.00 32 | 34.117.121.106,80,80,false,SIN,Asia Pacific,SG,Singapore,114,0.00 33 | 34.93.226.165,80,80,false,BOM,Asia Pacific,IN,Mumbai,121,0.00 34 | 34.93.79.152,80,80,false,BOM,Asia Pacific,IN,Mumbai,135,0.00 35 | 34.93.79.152,80,80,false,BOM,Asia Pacific,IN,Mumbai,137,0.00 36 | 34.160.118.77,80,80,false,SIN,Asia Pacific,SG,Singapore,138,0.00 37 | 34.111.135.19,80,80,false,SIN,Asia Pacific,SG,Singapore,141,0.00 38 | 34.117.254.68,80,80,false,SIN,Asia Pacific,SG,Singapore,144,0.00 39 | 34.36.237.86,80,80,false,SIN,Asia Pacific,SG,Singapore,145,0.00 40 | 34.93.226.165,80,80,false,BOM,Asia Pacific,IN,Mumbai,147,0.00 41 | 35.201.107.185,80,80,false,SIN,Asia Pacific,SG,Singapore,150,0.00 42 | 35.197.89.213,80,80,false,SEA,North America,US,Seattle,158,0.00 43 | 34.82.76.253,80,80,false,SEA,North America,US,Seattle,158,0.00 44 | 34.82.76.253,80,80,false,SEA,North America,US,Seattle,160,0.00 45 | 34.147.101.117,2082,80,false,AMS,Europe,NL,Amsterdam,160,0.00 46 | 34.147.101.117,2052,80,false,AMS,Europe,NL,Amsterdam,161,0.00 47 | 34.147.101.117,2087,80,false,SYD,Oceania,AU,Sydney,161,0.00 48 | 34.141.221.33,2083,80,false,AMS,Europe,NL,Amsterdam,162,0.00 49 | 34.141.221.33,2087,80,false,AMS,Europe,NL,Amsterdam,162,0.00 50 | 34.83.75.76,80,80,false,SEA,North America,US,Seattle,163,0.00 51 | 34.105.49.234,80,80,false,SEA,North America,US,Seattle,164,0.00 52 | 34.147.101.117,2083,80,false,AMS,Europe,NL,Amsterdam,164,0.00 53 | 34.147.101.117,2096,80,false,AMS,Europe,NL,Amsterdam,164,0.00 54 | 34.147.101.117,2083,80,false,AMS,Europe,NL,Amsterdam,164,0.00 55 | 34.147.101.117,2087,80,false,SYD,Oceania,AU,Sydney,164,0.00 56 | 34.82.76.253,80,80,false,SEA,North America,US,Seattle,165,0.00 57 | 34.147.82.194,2053,80,false,AMS,Europe,NL,Amsterdam,165,0.00 58 | 34.147.101.117,2053,80,false,AMS,Europe,NL,Amsterdam,166,0.00 59 | 34.141.221.33,2086,80,false,AMS,Europe,NL,Amsterdam,166,0.00 60 | 34.82.76.253,80,80,false,SEA,North America,US,Seattle,166,0.00 61 | 34.120.230.33,80,80,false,SIN,Asia Pacific,SG,Singapore,166,0.00 62 | 34.141.248.249,2096,80,false,AMS,Europe,NL,Amsterdam,168,0.00 63 | 34.147.101.117,2086,80,false,AMS,Europe,NL,Amsterdam,169,0.00 64 | 34.141.221.33,2082,80,false,AMS,Europe,NL,Amsterdam,172,0.00 65 | 34.141.248.249,2083,80,false,AMS,Europe,NL,Amsterdam,174,0.00 66 | 34.93.226.165,80,80,false,BOM,Asia Pacific,IN,Mumbai,175,0.00 67 | 35.200.202.118,80,80,false,BOM,Asia Pacific,IN,Mumbai,178,0.00 68 | 34.147.101.117,2053,80,false,AMS,Europe,NL,Amsterdam,179,0.00 69 | 34.147.101.117,2086,80,false,AMS,Europe,NL,Amsterdam,179,0.00 70 | 34.105.49.234,80,80,false,SEA,North America,US,Seattle,180,0.00 71 | 34.141.221.33,2053,80,false,AMS,Europe,NL,Amsterdam,181,0.00 72 | 34.117.236.246,80,80,false,SIN,Asia Pacific,SG,Singapore,182,0.00 73 | 34.141.248.249,2082,80,false,AMS,Europe,NL,Amsterdam,183,0.00 74 | 34.147.101.117,2095,80,false,AMS,Europe,NL,Amsterdam,183,0.00 75 | 34.83.75.76,80,80,false,SEA,North America,US,Seattle,183,0.00 76 | 34.141.248.249,2087,80,false,AMS,Europe,NL,Amsterdam,190,0.00 77 | 34.141.248.249,2086,80,false,AMS,Europe,NL,Amsterdam,190,0.00 78 | 34.141.221.33,2095,80,false,AMS,Europe,NL,Amsterdam,193,0.00 79 | 35.200.202.118,80,80,false,BOM,Asia Pacific,IN,Mumbai,198,0.00 80 | 34.147.101.117,2052,80,false,AMS,Europe,NL,Amsterdam,199,0.00 81 | 35.247.124.181,80,80,false,SEA,North America,US,Seattle,199,0.00 82 | 34.111.103.7,80,80,false,SIN,Asia Pacific,SG,Singapore,200,0.00 83 | 34.147.101.117,2096,80,false,AMS,Europe,NL,Amsterdam,202,0.00 84 | 34.56.143.151,80,8080,false,ORD,North America,US,Chicago,207,0.00 85 | 34.44.121.135,8080,80,false,ORD,North America,US,Chicago,207,0.00 86 | 34.105.49.234,80,80,false,SEA,North America,US,Seattle,208,0.00 87 | 35.202.49.74,80,80,false,ORD,North America,US,Chicago,209,0.00 88 | 35.202.49.74,80,80,false,ORD,North America,US,Chicago,209,0.00 89 | 34.44.121.135,8080,80,false,ORD,North America,US,Chicago,211,0.00 90 | 34.147.101.117,2095,80,false,AMS,Europe,NL,Amsterdam,217,0.00 91 | 35.232.39.116,80,80,false,EWR,North America,US,Newark,217,0.00 92 | 34.138.228.64,80,80,false,ATL,North America,US,Atlanta,218,0.00 93 | 34.141.248.249,2052,80,false,AMS,Europe,NL,Amsterdam,220,0.00 94 | 34.74.105.17,80,8080,false,ATL,North America,US,Atlanta,221,0.00 95 | 34.83.75.76,80,80,false,SEA,North America,US,Seattle,222,0.00 96 | 35.188.87.85,80,8080,false,ORD,North America,US,Chicago,223,0.00 97 | 34.147.82.194,2053,80,false,AMS,Europe,NL,Amsterdam,224,0.00 98 | 34.141.221.33,2096,80,false,AMS,Europe,NL,Amsterdam,226,0.00 99 | 34.56.143.151,80,8080,false,ORD,North America,US,Chicago,226,0.00 100 | 104.196.221.253,80,80,false,IAD,North America,US,Ashburn,229,0.00 101 | 34.147.101.117,2082,80,false,AMS,Europe,NL,Amsterdam,234,0.00 102 | 34.74.105.17,80,8080,false,ATL,North America,US,Atlanta,234,0.00 103 | 34.141.221.33,2052,80,false,AMS,Europe,NL,Amsterdam,235,0.00 104 | 35.202.49.74,80,80,false,ORD,North America,US,Chicago,238,0.00 105 | 34.74.105.17,80,8080,false,ATL,North America,US,Atlanta,246,0.00 106 | 34.83.75.76,80,80,false,SEA,North America,US,Seattle,253,0.00 107 | 35.187.41.171,2083,80,false,CDG,Europe,FR,Paris,272,0.00 108 | -------------------------------------------------------------------------------- /TestData/1.csv: -------------------------------------------------------------------------------- 1 | IP地址,数据中心,国家代码,区域,城市,延迟, 2 | stumptowncoffee.com,SJC,US,North America,San Jose,352 ms, 3 | omnicoreagency.com,SJC,US,North America,San Jose,353 ms, 4 | harney.com,SJC,US,North America,San Jose,353 ms, 5 | casper.com,SJC,US,North America,San Jose,354 ms, 6 | eber.co,SJC,US,North America,San Jose,355 ms, 7 | totalenergies.com,SJC,US,North America,San Jose,355 ms, 8 | farmaid.org,SJC,US,North America,San Jose,355 ms, 9 | silkandsonder.com,SJC,US,North America,San Jose,357 ms, 10 | natashaskitchen.com,SJC,US,North America,San Jose,357 ms, 11 | mq.edu.au,SJC,US,North America,San Jose,358 ms, 12 | war.ukraine.ua,SJC,US,North America,San Jose,359 ms, 13 | sinarharian.com.my,SJC,US,North America,San Jose,359 ms, 14 | nomadicmatt.com,SJC,US,North America,San Jose,360 ms, 15 | languagetool.org,SJC,US,North America,San Jose,361 ms, 16 | omio.com,SJC,US,North America,San Jose,361 ms, 17 | covid19.who.int,SJC,US,North America,San Jose,362 ms, 18 | griffith.edu.au,SJC,US,North America,San Jose,363 ms, 19 | gradle.org,SJC,US,North America,San Jose,363 ms, 20 | bitcoinmagazine.com,SJC,US,North America,San Jose,363 ms, 21 | cdn.pixabay.com,SJC,US,North America,San Jose,363 ms, 22 | payfast.io,SJC,US,North America,San Jose,364 ms, 23 | pdf.sciencedirectassets.com,SJC,US,North America,San Jose,364 ms, 24 | just-eat.co.uk,SJC,US,North America,San Jose,364 ms, 25 | epubs.siam.org,SJC,US,North America,San Jose,364 ms, 26 | grailed.com,SJC,US,North America,San Jose,364 ms, 27 | cdn.embedly.com,SJC,US,North America,San Jose,365 ms, 28 | papress.com,SJC,US,North America,San Jose,366 ms, 29 | morningbrew.com,SJC,US,North America,San Jose,366 ms, 30 | shoutem.com,SJC,US,North America,San Jose,366 ms, 31 | dstv.com,SJC,US,North America,San Jose,366 ms, 32 | blogs.vmware.com,SJC,US,North America,San Jose,367 ms, 33 | aem.asm.org,SJC,US,North America,San Jose,367 ms, 34 | instant.page,SJC,US,North America,San Jose,368 ms, 35 | help.figma.com,SJC,US,North America,San Jose,368 ms, 36 | kith.com,SJC,US,North America,San Jose,369 ms, 37 | emedicine.medscape.com,SJC,US,North America,San Jose,369 ms, 38 | phantom.app,SJC,US,North America,San Jose,369 ms, 39 | pinksaltwall.com,SJC,US,North America,San Jose,369 ms, 40 | instantpot.com,SJC,US,North America,San Jose,370 ms, 41 | theawesomer.com,SJC,US,North America,San Jose,370 ms, 42 | faceit.com,SJC,US,North America,San Jose,370 ms, 43 | turo.com,SJC,US,North America,San Jose,370 ms, 44 | travel.stackexchange.com,SJC,US,North America,San Jose,370 ms, 45 | springwise.com,SJC,US,North America,San Jose,371 ms, 46 | ecfr.eu,HKG,HK,Asia Pacific,Hong Kong,371 ms, 47 | community.spiceworks.com,SJC,US,North America,San Jose,372 ms, 48 | switch-bot.com,SJC,US,North America,San Jose,372 ms, 49 | thegadgetflow.com,HKG,HK,Asia Pacific,Hong Kong,372 ms, 50 | actu.fr,HKG,HK,Asia Pacific,Hong Kong,372 ms, 51 | rateyourmusic.com,SJC,US,North America,San Jose,373 ms, 52 | databank.worldbank.org,SJC,US,North America,San Jose,373 ms, 53 | tribune.com.pk,SJC,US,North America,San Jose,373 ms, 54 | orientaltrading.com,SJC,US,North America,San Jose,374 ms, 55 | flask.palletsprojects.com,SJC,US,North America,San Jose,374 ms, 56 | euobserver.com,SJC,US,North America,San Jose,374 ms, 57 | allaboutcircuits.com,SJC,US,North America,San Jose,374 ms, 58 | blog.jquery.com,SJC,US,North America,San Jose,374 ms, 59 | baeldung.com,SJC,US,North America,San Jose,375 ms, 60 | selleckchem.com,SJC,US,North America,San Jose,375 ms, 61 | orbit.dtu.dk,SJC,US,North America,San Jose,375 ms, 62 | isi.edu,SJC,US,North America,San Jose,376 ms, 63 | namshi.com,SJC,US,North America,San Jose,376 ms, 64 | indeed.co.uk,SJC,US,North America,San Jose,376 ms, 65 | name.com,SJC,US,North America,San Jose,376 ms, 66 | ogilvy.com,SJC,US,North America,San Jose,377 ms, 67 | bikeexif.com,SJC,US,North America,San Jose,377 ms, 68 | franklintempleton.com,SJC,US,North America,San Jose,377 ms, 69 | gearpatrol.com,HKG,HK,Asia Pacific,Hong Kong,377 ms, 70 | iloveimg.com,SJC,US,North America,San Jose,377 ms, 71 | research.manchester.ac.uk,SJC,US,North America,San Jose,378 ms, 72 | basketball-reference.com,SJC,US,North America,San Jose,378 ms, 73 | support.giphy.com,SJC,US,North America,San Jose,378 ms, 74 | arc.aiaa.org,SJC,US,North America,San Jose,379 ms, 75 | instapage.com,SJC,US,North America,San Jose,379 ms, 76 | giaohangtietkiem.vn,HKG,HK,Asia Pacific,Hong Kong,379 ms, 77 | bamboohr.com,SJC,US,North America,San Jose,379 ms, 78 | pubs.aeaweb.org,SJC,US,North America,San Jose,379 ms, 79 | developers.slashdot.org,SJC,US,North America,San Jose,379 ms, 80 | audacity.sourceforge.net,SJC,US,North America,San Jose,380 ms, 81 | credobeauty.com,SJC,US,North America,San Jose,381 ms, 82 | wordcounter.net,SJC,US,North America,San Jose,381 ms, 83 | funko.com,SJC,US,North America,San Jose,382 ms, 84 | support.hostinger.com,SJC,US,North America,San Jose,383 ms, 85 | capitalizemytitle.com,SJC,US,North America,San Jose,384 ms, 86 | taskade.com,HKG,HK,Asia Pacific,Hong Kong,384 ms, 87 | languages.oup.com,SJC,US,North America,San Jose,384 ms, 88 | nltimes.nl,SJC,US,North America,San Jose,384 ms, 89 | unix.stackexchange.com,SJC,US,North America,San Jose,385 ms, 90 | digitalriver.com,SJC,US,North America,San Jose,385 ms, 91 | clubspark.lta.org.uk,SJC,US,North America,San Jose,385 ms, 92 | swedenabroad.se,SJC,US,North America,San Jose,385 ms, 93 | whfoods.com,SJC,US,North America,San Jose,385 ms, 94 | mercatus.org,SJC,US,North America,San Jose,386 ms, 95 | kw.com,LAX,US,North America,Los Angeles,386 ms, 96 | infusionsoft.com,SJC,US,North America,San Jose,386 ms, 97 | news.berkeley.edu,SJC,US,North America,San Jose,387 ms, 98 | jwplayer.com,SJC,US,North America,San Jose,387 ms, 99 | audius.co,HKG,HK,Asia Pacific,Hong Kong,387 ms, 100 | escapistmagazine.com,HKG,HK,Asia Pacific,Hong Kong,387 ms, 101 | esajournals.onlinelibrary.wiley.com,SJC,US,North America,San Jose,387 ms, 102 | medicine.umich.edu,SJC,US,North America,San Jose,388 ms, 103 | optipng.sourceforge.net,SJC,US,North America,San Jose,389 ms, 104 | business.udemy.com,SJC,US,North America,San Jose,389 ms, 105 | opencv.org,SJC,US,North America,San Jose,390 ms, 106 | journalofethics.ama-assn.org,SJC,US,North America,San Jose,390 ms, 107 | newsis.com,SJC,US,North America,San Jose,390 ms, 108 | officesnapshots.com,SJC,US,North America,San Jose,390 ms, 109 | sphero.com,SJC,US,North America,San Jose,391 ms, 110 | anthrosource.onlinelibrary.wiley.com,SJC,US,North America,San Jose,391 ms, 111 | queue.acm.org,HKG,HK,Asia Pacific,Hong Kong,391 ms, 112 | tech.slashdot.org,SJC,US,North America,San Jose,392 ms, 113 | barn2.co.uk,SJC,US,North America,San Jose,392 ms, 114 | forums.macrumors.com,HKG,HK,Asia Pacific,Hong Kong,392 ms, 115 | dexerto.com,HKG,HK,Asia Pacific,Hong Kong,392 ms, 116 | simplified.com,HKG,HK,Asia Pacific,Hong Kong,394 ms, 117 | scanbot.io,SJC,US,North America,San Jose,394 ms, 118 | cftc.gov,HKG,HK,Asia Pacific,Hong Kong,395 ms, 119 | corsair.com,HKG,HK,Asia Pacific,Hong Kong,399 ms, 120 | visionofhumanity.org,SJC,US,North America,San Jose,400 ms, 121 | techbullion.com,SJC,US,North America,San Jose,402 ms, 122 | www-nds.iaea.org,HKG,HK,Asia Pacific,Hong Kong,402 ms, 123 | cybersource.com,SJC,US,North America,San Jose,402 ms, 124 | physio-pedia.com,HKG,HK,Asia Pacific,Hong Kong,402 ms, 125 | devops.com,HKG,HK,Asia Pacific,Hong Kong,406 ms, 126 | sedo.com,SJC,US,North America,San Jose,409 ms, 127 | nationaltoday.com,SJC,US,North America,San Jose,410 ms, 128 | linkpop.com,SJC,US,North America,San Jose,410 ms, 129 | dev.maxmind.com,SJC,US,North America,San Jose,411 ms, 130 | gothamist.com,HKG,HK,Asia Pacific,Hong Kong,411 ms, 131 | moodys.com,HKG,HK,Asia Pacific,Hong Kong,413 ms, 132 | sangoma.com,SJC,US,North America,San Jose,416 ms, 133 | 10web.io,HKG,HK,Asia Pacific,Hong Kong,416 ms, 134 | sos.state.co.us,HKG,HK,Asia Pacific,Hong Kong,417 ms, 135 | afi.com,HKG,HK,Asia Pacific,Hong Kong,420 ms, 136 | mistral.ai,HKG,HK,Asia Pacific,Hong Kong,422 ms, 137 | awards.acm.org,HKG,HK,Asia Pacific,Hong Kong,426 ms, 138 | blogfreely.net,CDG,FR,Europe,Paris,428 ms, 139 | undraw.co,LHR,GB,Europe,London,430 ms, 140 | copy.com,LHR,GB,Europe,London,434 ms, 141 | wellesley.edu,SJC,US,North America,San Jose,435 ms, 142 | wideopencountry.com,LHR,GB,Europe,London,441 ms, 143 | medium.design,SJC,US,North America,San Jose,443 ms, 144 | scaledagileframework.com,SJC,US,North America,San Jose,449 ms, 145 | plosmedicine.org,CDG,FR,Europe,Paris,454 ms, 146 | powerreviews.com,SJC,US,North America,San Jose,454 ms, 147 | delyva.com,LHR,GB,Europe,London,456 ms, 148 | demotix.com,LHR,GB,Europe,London,458 ms, 149 | vanguardngr.com,HKG,HK,Asia Pacific,Hong Kong,458 ms, 150 | trekearth.com,CDG,FR,Europe,Paris,459 ms, 151 | codyhouse.co,LHR,GB,Europe,London,459 ms, 152 | ekantipur.com,AMS,NL,Europe,Amsterdam,459 ms, 153 | supercars.net,CDG,FR,Europe,Paris,462 ms, 154 | remoteok.io,LHR,GB,Europe,London,463 ms, 155 | atlantico.fr,LHR,GB,Europe,London,468 ms, 156 | nicekicks.com,CDG,FR,Europe,Paris,470 ms, 157 | courttv.com,SJC,US,North America,San Jose,471 ms, 158 | postheaven.net,LHR,GB,Europe,London,477 ms, 159 | newsblaze.com,IAD,US,North America,Ashburn,477 ms, 160 | hifructose.com,LHR,GB,Europe,London,478 ms, 161 | americansongwriter.com,CDG,FR,Europe,Paris,478 ms, 162 | todayszaman.com,CDG,FR,Europe,Paris,479 ms, 163 | osnews.com,LHR,GB,Europe,London,480 ms, 164 | slowly.app,LHR,GB,Europe,London,481 ms, 165 | dancemagazine.com,IAD,US,North America,Ashburn,481 ms, 166 | v3.co.uk,AMS,NL,Europe,Amsterdam,481 ms, 167 | researchsquare.com,HKG,HK,Asia Pacific,Hong Kong,481 ms, 168 | grpc.io,CDG,FR,Europe,Paris,482 ms, 169 | support.credly.com,SJC,US,North America,San Jose,484 ms, 170 | insideclimatenews.org,LHR,GB,Europe,London,486 ms, 171 | techpresident.com,CDG,FR,Europe,Paris,486 ms, 172 | theeverygirl.com,IAD,US,North America,Ashburn,488 ms, 173 | authorityhacker.com,IAD,US,North America,Ashburn,490 ms, 174 | unlicense.org,CDG,FR,Europe,Paris,492 ms, 175 | houstonpress.com,CDG,FR,Europe,Paris,493 ms, 176 | theins.ru,IAD,US,North America,Ashburn,493 ms, 177 | static.addtoany.com,IAD,US,North America,Ashburn,493 ms, 178 | publicintegrity.org,IAD,US,North America,Ashburn,495 ms, 179 | doxygen.nl,CDG,FR,Europe,Paris,495 ms, 180 | amcharts.com,IAD,US,North America,Ashburn,497 ms, 181 | webpack.js.org,IAD,US,North America,Ashburn,498 ms, 182 | calculator-online.net,IAD,US,North America,Ashburn,499 ms, 183 | bruegel.org,LHR,GB,Europe,London,504 ms, 184 | io9.gizmodo.com,IAD,US,North America,Ashburn,505 ms, 185 | gratisography.com,LHR,GB,Europe,London,505 ms, 186 | webchat.freenode.net,IAD,US,North America,Ashburn,506 ms, 187 | kennedy-center.org,IAD,US,North America,Ashburn,507 ms, 188 | animalpolitico.com,IAD,US,North America,Ashburn,507 ms, 189 | waltonbd.com,CDG,FR,Europe,Paris,508 ms, 190 | datasociety.net,IAD,US,North America,Ashburn,511 ms, 191 | bdnews24.com,CDG,FR,Europe,Paris,511 ms, 192 | keepassxc.org,CDG,FR,Europe,Paris,512 ms, 193 | emailoctopus.com,IAD,US,North America,Ashburn,513 ms, 194 | radiopaedia.org,IAD,US,North America,Ashburn,514 ms, 195 | moddb.com,IAD,US,North America,Ashburn,516 ms, 196 | ratchakitcha.soc.go.th,IAD,US,North America,Ashburn,518 ms, 197 | universetoday.com,CDG,FR,Europe,Paris,519 ms, 198 | miaminewtimes.com,IAD,US,North America,Ashburn,519 ms, 199 | publicdomainpictures.net,IAD,US,North America,Ashburn,520 ms, 200 | linux.die.net,IAD,US,North America,Ashburn,520 ms, 201 | asq.org,IAD,US,North America,Ashburn,521 ms, 202 | clustrmaps.com,IAD,US,North America,Ashburn,524 ms, 203 | developers.hubspot.com,SJC,US,North America,San Jose,525 ms, 204 | matplotlib.org,IAD,US,North America,Ashburn,525 ms, 205 | dict.longdo.com,IAD,US,North America,Ashburn,525 ms, 206 | getclicky.com,SJC,US,North America,San Jose,527 ms, 207 | zopim.com,SJC,US,North America,San Jose,528 ms, 208 | invespcro.com,LHR,GB,Europe,London,528 ms, 209 | open.buffer.com,SJC,US,North America,San Jose,530 ms, 210 | metropolismag.com,IAD,US,North America,Ashburn,530 ms, 211 | vvmf.org,IAD,US,North America,Ashburn,531 ms, 212 | chatelaine.com,SJC,US,North America,San Jose,532 ms, 213 | jeuxvideo.com,SJC,US,North America,San Jose,533 ms, 214 | wagwalking.com,SJC,US,North America,San Jose,533 ms, 215 | blabbermouth.net,IAD,US,North America,Ashburn,534 ms, 216 | tastykitchen.com,IAD,US,North America,Ashburn,535 ms, 217 | talabat.com,SJC,US,North America,San Jose,535 ms, 218 | ar15.com,IAD,US,North America,Ashburn,537 ms, 219 | msc.org,IAD,US,North America,Ashburn,537 ms, 220 | aflcio.org,IAD,US,North America,Ashburn,537 ms, 221 | urbanclap.com,SJC,US,North America,San Jose,538 ms, 222 | themarshallproject.org,IAD,US,North America,Ashburn,540 ms, 223 | thesartorialist.com,IAD,US,North America,Ashburn,540 ms, 224 | yithemes.com,SJC,US,North America,San Jose,540 ms, 225 | younghouselove.com,IAD,US,North America,Ashburn,540 ms, 226 | getrocketbook.com,SJC,US,North America,San Jose,541 ms, 227 | meistertask.com,SJC,US,North America,San Jose,542 ms, 228 | travelexinsurance.com,IAD,US,North America,Ashburn,543 ms, 229 | garden.org,IAD,US,North America,Ashburn,545 ms, 230 | spoonuniversity.com,IAD,US,North America,Ashburn,545 ms, 231 | leadconnectorhq.com,HKG,HK,Asia Pacific,Hong Kong,546 ms, 232 | medium.freecodecamp.org,IAD,US,North America,Ashburn,546 ms, 233 | royal.uk,IAD,US,North America,Ashburn,547 ms, 234 | openmaptiles.org,LHR,GB,Europe,London,550 ms, 235 | axieinfinity.com,IAD,US,North America,Ashburn,551 ms, 236 | cinemark.com,SJC,US,North America,San Jose,551 ms, 237 | expressjs.com,IAD,US,North America,Ashburn,553 ms, 238 | opendoor.com,SJC,US,North America,San Jose,554 ms, 239 | wireshark.org,IAD,US,North America,Ashburn,554 ms, 240 | creativepro.com,IAD,US,North America,Ashburn,555 ms, 241 | airalo.com,HKG,HK,Asia Pacific,Hong Kong,555 ms, 242 | db-ip.com,IAD,US,North America,Ashburn,555 ms, 243 | azuremagazine.com,SJC,US,North America,San Jose,555 ms, 244 | gvsu.edu,SJC,US,North America,San Jose,557 ms, 245 | pcgamesn.com,IAD,US,North America,Ashburn,557 ms, 246 | newswise.com,SJC,US,North America,San Jose,560 ms, 247 | santabarbaraca.com,SJC,US,North America,San Jose,560 ms, 248 | disneyfoodblog.com,SJC,US,North America,San Jose,561 ms, 249 | israelnationalnews.com,SJC,US,North America,San Jose,561 ms, 250 | site.adform.com,IAD,US,North America,Ashburn,564 ms, 251 | skagen.com,SJC,US,North America,San Jose,565 ms, 252 | workable.com,SJC,US,North America,San Jose,566 ms, 253 | fpf.org,IAD,US,North America,Ashburn,567 ms, 254 | globalbankingandfinance.com,SJC,US,North America,San Jose,567 ms, 255 | memphistravel.com,SJC,US,North America,San Jose,569 ms, 256 | inchcalculator.com,HKG,HK,Asia Pacific,Hong Kong,570 ms, 257 | appuals.com,IAD,US,North America,Ashburn,570 ms, 258 | marketo.com,SJC,US,North America,San Jose,572 ms, 259 | domaintools.com,SJC,US,North America,San Jose,573 ms, 260 | ratedpeople.com,SJC,US,North America,San Jose,573 ms, 261 | worldcoin.org,HKG,HK,Asia Pacific,Hong Kong,573 ms, 262 | visitljubljana.com,SJC,US,North America,San Jose,575 ms, 263 | directory.libsyn.com,HKG,HK,Asia Pacific,Hong Kong,577 ms, 264 | bloomreach.com,SJC,US,North America,San Jose,578 ms, 265 | elsiglodetorreon.com.mx,SJC,US,North America,San Jose,583 ms, 266 | thelocal.se,HKG,HK,Asia Pacific,Hong Kong,584 ms, 267 | eniro.se,SJC,US,North America,San Jose,586 ms, 268 | vault.fbi.gov,SJC,US,North America,San Jose,586 ms, 269 | fooddive.com,HKG,HK,Asia Pacific,Hong Kong,587 ms, 270 | alza.cz,SJC,US,North America,San Jose,588 ms, 271 | writeablog.net,LHR,GB,Europe,London,589 ms, 272 | m.signalvnoise.com,HKG,HK,Asia Pacific,Hong Kong,590 ms, 273 | tcl.fr,SJC,US,North America,San Jose,590 ms, 274 | absolute.com,HKG,HK,Asia Pacific,Hong Kong,601 ms, 275 | whatscookingamerica.net,CDG,FR,Europe,Paris,602 ms, 276 | amaro.com,SJC,US,North America,San Jose,614 ms, 277 | aes.org,CDG,FR,Europe,Paris,621 ms, 278 | weareher.com,SJC,US,North America,San Jose,622 ms, 279 | littlethings.com,CDG,FR,Europe,Paris,634 ms, 280 | buytaert.net,CDG,FR,Europe,Paris,634 ms, 281 | mediaite.com,HKG,HK,Asia Pacific,Hong Kong,636 ms, 282 | c.ymcdn.com,LHR,GB,Europe,London,639 ms, 283 | idratherbewriting.com,LHR,GB,Europe,London,646 ms, 284 | nationstates.net,IAD,US,North America,Ashburn,648 ms, 285 | bitcatcha.com,LHR,GB,Europe,London,653 ms, 286 | techie-buzz.com,LHR,GB,Europe,London,661 ms, 287 | bubok.es,LHR,GB,Europe,London,663 ms, 288 | geeksaresexy.net,LHR,GB,Europe,London,664 ms, 289 | bigpictureclasses.com,IAD,US,North America,Ashburn,666 ms, 290 | privacypolicies.com,IAD,US,North America,Ashburn,668 ms, 291 | improvely.com,IAD,US,North America,Ashburn,669 ms, 292 | bigfinish.com,IAD,US,North America,Ashburn,672 ms, 293 | firstsiteguide.com,LHR,GB,Europe,London,674 ms, 294 | happi.com,IAD,US,North America,Ashburn,681 ms, 295 | ancient.eu,LHR,GB,Europe,London,681 ms, 296 | docs.mathjax.org,SJC,US,North America,San Jose,682 ms, 297 | airtasker.com,SJC,US,North America,San Jose,683 ms, 298 | futura-sciences.com,SJC,US,North America,San Jose,689 ms, 299 | synthtopia.com,LHR,GB,Europe,London,690 ms, 300 | audio-technica.com,IAD,US,North America,Ashburn,691 ms, 301 | gamedeveloper.com,SJC,US,North America,San Jose,692 ms, 302 | carrefour.com,SJC,US,North America,San Jose,694 ms, 303 | enoughproject.org,SJC,US,North America,San Jose,695 ms, 304 | museumsvictoria.com.au,HKG,HK,Asia Pacific,Hong Kong,695 ms, 305 | myvue.com,SJC,US,North America,San Jose,696 ms, 306 | sears.com,SJC,US,North America,San Jose,699 ms, 307 | cybersecuritydive.com,SJC,US,North America,San Jose,699 ms, 308 | localwp.com,HKG,HK,Asia Pacific,Hong Kong,699 ms, 309 | torontoist.com,CDG,FR,Europe,Paris,700 ms, 310 | memory.loc.gov,SJC,US,North America,San Jose,700 ms, 311 | pro-football-reference.com,SJC,US,North America,San Jose,701 ms, 312 | softwaretestinghelp.com,IAD,US,North America,Ashburn,702 ms, 313 | websummit.com,IAD,US,North America,Ashburn,707 ms, 314 | capitalfactory.com,SJC,US,North America,San Jose,709 ms, 315 | cpr.org,HKG,HK,Asia Pacific,Hong Kong,711 ms, 316 | archive.constantcontact.com,SJC,US,North America,San Jose,712 ms, 317 | panynj.gov,HKG,HK,Asia Pacific,Hong Kong,713 ms, 318 | rasamalaysia.com,SJC,US,North America,San Jose,714 ms, 319 | billetweb.fr,IAD,US,North America,Ashburn,714 ms, 320 | rstb.royalsocietypublishing.org,SJC,US,North America,San Jose,714 ms, 321 | wemakeit.com,SJC,US,North America,San Jose,714 ms, 322 | junit.org,HKG,HK,Asia Pacific,Hong Kong,714 ms, 323 | redpixel.com,IAD,US,North America,Ashburn,715 ms, 324 | smashburger.com,SJC,US,North America,San Jose,716 ms, 325 | visaeurope.com,IAD,US,North America,Ashburn,717 ms, 326 | atariage.com,IAD,US,North America,Ashburn,719 ms, 327 | familyhandyman.com,LAX,US,North America,Los Angeles,719 ms, 328 | helpdesk.com,SJC,US,North America,San Jose,719 ms, 329 | whiteonricecouple.com,SJC,US,North America,San Jose,720 ms, 330 | simplehabit.com,IAD,US,North America,Ashburn,720 ms, 331 | codeigniter.com,SJC,US,North America,San Jose,722 ms, 332 | crcpress.com,SJC,US,North America,San Jose,722 ms, 333 | undrr.org,HKG,HK,Asia Pacific,Hong Kong,722 ms, 334 | getmyinvoices.com,IAD,US,North America,Ashburn,723 ms, 335 | birgun.net,IAD,US,North America,Ashburn,723 ms, 336 | fiercebiotech.com,SJC,US,North America,San Jose,724 ms, 337 | cupofjo.com,SJC,US,North America,San Jose,725 ms, 338 | alsumaria.tv,IAD,US,North America,Ashburn,730 ms, 339 | geek.com,SJC,US,North America,San Jose,731 ms, 340 | 750g.com,SJC,US,North America,San Jose,734 ms, 341 | uship.com,LAX,US,North America,Los Angeles,737 ms, 342 | wpbeaverbuilder.com,IAD,US,North America,Ashburn,738 ms, 343 | afdb.org,HKG,HK,Asia Pacific,Hong Kong,740 ms, 344 | portfolium.com,SJC,US,North America,San Jose,740 ms, 345 | gohighlevel.com,SJC,US,North America,San Jose,742 ms, 346 | intelligentchange.com,SJC,US,North America,San Jose,743 ms, 347 | ncwit.org,LHR,GB,Europe,London,751 ms, 348 | nuleafnaturals.com,SJC,US,North America,San Jose,767 ms, 349 | mysqltutorial.org,CDG,FR,Europe,Paris,768 ms, 350 | kronos.com,HKG,HK,Asia Pacific,Hong Kong,770 ms, 351 | pubhtml5.com,HKG,HK,Asia Pacific,Hong Kong,770 ms, 352 | saturn.de,MAD,ES,Europe,Madrid,771 ms, 353 | wplms.io,LHR,GB,Europe,London,772 ms, 354 | feyenoord.nl,HKG,HK,Asia Pacific,Hong Kong,773 ms, 355 | nvca.org,LHR,GB,Europe,London,774 ms, 356 | ilmeteo.it,HKG,HK,Asia Pacific,Hong Kong,776 ms, 357 | sca.org,LHR,GB,Europe,London,776 ms, 358 | fontsplugin.com,LHR,GB,Europe,London,776 ms, 359 | jango.com,HKG,HK,Asia Pacific,Hong Kong,780 ms, 360 | debounce.io,LHR,GB,Europe,London,781 ms, 361 | addgene.org,SJC,US,North America,San Jose,782 ms, 362 | colourblindawareness.org,LHR,GB,Europe,London,786 ms, 363 | oie.int,CDG,FR,Europe,Paris,789 ms, 364 | blog.gitnux.com,LHR,GB,Europe,London,794 ms, 365 | example.co.uk,CDG,FR,Europe,Paris,798 ms, 366 | linuxbabe.com,LHR,GB,Europe,London,798 ms, 367 | bitbin.it,CDG,FR,Europe,Paris,798 ms, 368 | wook.pt,HKG,HK,Asia Pacific,Hong Kong,802 ms, 369 | transequality.org,LHR,GB,Europe,London,803 ms, 370 | epic.org,IAD,US,North America,Ashburn,804 ms, 371 | acouplecooks.com,CDG,FR,Europe,Paris,805 ms, 372 | newschallenge.org,CDG,FR,Europe,Paris,806 ms, 373 | africultures.com,LHR,GB,Europe,London,807 ms, 374 | visualcomposer.com,CDG,FR,Europe,Paris,809 ms, 375 | unimelb.edu.au,SJC,US,North America,San Jose,810 ms, 376 | catalyst.org,HKG,HK,Asia Pacific,Hong Kong,812 ms, 377 | seomoz.org,LHR,GB,Europe,London,814 ms, 378 | joxi.ru,LHR,GB,Europe,London,815 ms, 379 | thymio.org,LHR,GB,Europe,London,815 ms, 380 | aas.org,IAD,US,North America,Ashburn,816 ms, 381 | fubiz.net,CDG,FR,Europe,Paris,817 ms, 382 | ivpress.com,IAD,US,North America,Ashburn,821 ms, 383 | muslim-library.com,AMS,NL,Europe,Amsterdam,822 ms, 384 | readwriteweb.com,CDG,FR,Europe,Paris,823 ms, 385 | ajp.psychiatryonline.org,HKG,HK,Asia Pacific,Hong Kong,824 ms, 386 | paysera.com,IAD,US,North America,Ashburn,825 ms, 387 | allaboutbirds.org,IAD,US,North America,Ashburn,825 ms, 388 | momsdemandaction.org,IAD,US,North America,Ashburn,825 ms, 389 | plasq.com,CDG,FR,Europe,Paris,829 ms, 390 | theroundup.org,CDG,FR,Europe,Paris,829 ms, 391 | cowboysindians.com,IAD,US,North America,Ashburn,830 ms, 392 | awstats.org,AMS,NL,Europe,Amsterdam,831 ms, 393 | migrationpolicy.org,IAD,US,North America,Ashburn,836 ms, 394 | artfacts.net,IAD,US,North America,Ashburn,836 ms, 395 | pb.pl,IAD,US,North America,Ashburn,837 ms, 396 | myanmarwebdesigner.com,LHR,GB,Europe,London,841 ms, 397 | blog.okfn.org,IAD,US,North America,Ashburn,843 ms, 398 | leagle.com,IAD,US,North America,Ashburn,844 ms, 399 | taxpolicycenter.org,IAD,US,North America,Ashburn,851 ms, 400 | kyivindependent.com,IAD,US,North America,Ashburn,855 ms, 401 | ufile.io,IAD,US,North America,Ashburn,855 ms, 402 | eatthismuch.com,IAD,US,North America,Ashburn,857 ms, 403 | koko.org,IAD,US,North America,Ashburn,857 ms, 404 | vtiger.com,IAD,US,North America,Ashburn,862 ms, 405 | canalrural.com.br,IAD,US,North America,Ashburn,865 ms, 406 | bikeleague.org,LHR,GB,Europe,London,872 ms, 407 | perkins.org,IAD,US,North America,Ashburn,877 ms, 408 | worldscreen.com,IAD,US,North America,Ashburn,889 ms, 409 | changegrowlive.org,IAD,US,North America,Ashburn,891 ms, 410 | 350.org,LHR,GB,Europe,London,893 ms, 411 | birdguides.com,IAD,US,North America,Ashburn,899 ms, 412 | origo.hu,IAD,US,North America,Ashburn,909 ms, 413 | radarbox.com,IAD,US,North America,Ashburn,917 ms, 414 | hancinema.net,IAD,US,North America,Ashburn,918 ms, 415 | fraserinstitute.org,IAD,US,North America,Ashburn,927 ms, 416 | unescap.org,IAD,US,North America,Ashburn,942 ms, 417 | oxu.az,IAD,US,North America,Ashburn,953 ms, 418 | color.org,IAD,US,North America,Ashburn,954 ms, 419 | iledefrance-mobilites.fr,CDG,FR,Europe,Paris,973 ms, 420 | paidmembershipspro.com,IAD,US,North America,Ashburn,1091 ms, 421 | bhf.org.uk,IAD,US,North America,Ashburn,1181 ms, 422 | mackie.com,IAD,US,North America,Ashburn,1199 ms, 423 | electronicintifada.net,IAD,US,North America,Ashburn,1247 ms, 424 | nssf.org,SJC,US,North America,San Jose,1347 ms, 425 | forbes.com.mx,IAD,US,North America,Ashburn,1374 ms, 426 | fangamer.com,IAD,US,North America,Ashburn,1421 ms, 427 | thebulletin.org,IAD,US,North America,Ashburn,1512 ms, 428 | bmkg.go.id,IAD,US,North America,Ashburn,1650 ms, 429 | sos.wa.gov,SJC,US,North America,San Jose,1675 ms, 430 | cakewallet.com,IAD,US,North America,Ashburn,1751 ms, 431 | simplyhired.com,HKG,HK,Asia Pacific,Hong Kong,1759 ms, 432 | garyvaynerchuk.com,SJC,US,North America,San Jose,1867 ms, 433 | carbonfund.org,SJC,US,North America,San Jose,1869 ms, 434 | mist.com,SJC,US,North America,San Jose,1871 ms, 435 | hushed.com,SJC,US,North America,San Jose,1871 ms, 436 | cooperhewitt.org,SJC,US,North America,San Jose,1872 ms, 437 | eecs.berkeley.edu,SJC,US,North America,San Jose,1873 ms, 438 | clickbank.com,SJC,US,North America,San Jose,1874 ms, 439 | royalgorgebridge.com,SJC,US,North America,San Jose,1891 ms, 440 | verticalresponse.com,SJC,US,North America,San Jose,1894 ms, 441 | seia.org,SJC,US,North America,San Jose,1922 ms, 442 | blog.makezine.com,SJC,US,North America,San Jose,1954 ms, 443 | thenai.org,SJC,US,North America,San Jose,1971 ms, 444 | wpexplorer.com,SJC,US,North America,San Jose,1975 ms, 445 | midi.org,SJC,US,North America,San Jose,2078 ms, 446 | psecu.com,SJC,US,North America,San Jose,2741 ms, 447 | -------------------------------------------------------------------------------- /TestData/2.csv: -------------------------------------------------------------------------------- 1 | IP地址,端口,TLS,数据中心,地区,城市,网络延迟,下载速度 2 | 103.184.45.253,2053,true,SJC,North America,San Jose,169 ms,4291 kB/s 3 | 103.184.45.217,2053,true,SJC,North America,San Jose,163 ms,3670 kB/s 4 | 103.184.45.164,2053,true,SJC,North America,San Jose,190 ms,3591 kB/s 5 | 103.184.45.204,2053,true,SJC,North America,San Jose,160 ms,3019 kB/s 6 | 103.184.45.240,2053,true,SJC,North America,San Jose,168 ms,2981 kB/s 7 | 103.184.45.226,2053,true,SJC,North America,San Jose,162 ms,2961 kB/s 8 | 103.184.45.155,2053,true,SJC,North America,San Jose,175 ms,2830 kB/s 9 | 103.184.45.32,2053,true,SJC,North America,San Jose,165 ms,2811 kB/s 10 | 103.184.45.254,2053,true,SJC,North America,San Jose,178 ms,2776 kB/s 11 | 103.184.45.189,2053,true,SJC,North America,San Jose,197 ms,2664 kB/s 12 | 103.184.45.73,2053,true,SJC,North America,San Jose,196 ms,2604 kB/s 13 | 103.184.45.98,2053,true,SJC,North America,San Jose,223 ms,2595 kB/s 14 | 103.184.45.5,2053,true,SJC,North America,San Jose,155 ms,2588 kB/s 15 | 103.184.45.63,2053,true,SJC,North America,San Jose,203 ms,2517 kB/s 16 | 103.184.45.243,2053,true,SJC,North America,San Jose,170 ms,2497 kB/s 17 | 103.184.45.193,2053,true,SJC,North America,San Jose,187 ms,2481 kB/s 18 | 103.184.45.196,2053,true,SJC,North America,San Jose,197 ms,2475 kB/s 19 | 103.184.45.192,2053,true,SJC,North America,San Jose,214 ms,2462 kB/s 20 | 103.184.45.58,2053,true,SJC,North America,San Jose,191 ms,2454 kB/s 21 | 103.184.45.106,2053,true,SJC,North America,San Jose,213 ms,2453 kB/s 22 | 103.184.45.62,2053,true,SJC,North America,San Jose,212 ms,2409 kB/s 23 | 103.184.45.78,2053,true,SJC,North America,San Jose,214 ms,2396 kB/s 24 | 103.184.45.54,2053,true,SJC,North America,San Jose,162 ms,2382 kB/s 25 | 103.184.45.66,2053,true,SJC,North America,San Jose,204 ms,2365 kB/s 26 | 103.184.45.181,2053,true,SJC,North America,San Jose,191 ms,2359 kB/s 27 | 103.184.45.247,2053,true,SJC,North America,San Jose,173 ms,2321 kB/s 28 | 103.184.45.161,2053,true,SJC,North America,San Jose,170 ms,2321 kB/s 29 | 103.184.45.28,2053,true,SJC,North America,San Jose,167 ms,2281 kB/s 30 | 103.184.45.92,2053,true,SJC,North America,San Jose,222 ms,2268 kB/s 31 | 103.184.45.115,2053,true,SJC,North America,San Jose,213 ms,2259 kB/s 32 | 103.184.45.249,2053,true,SJC,North America,San Jose,167 ms,2248 kB/s 33 | 103.184.45.124,2053,true,SJC,North America,San Jose,213 ms,2220 kB/s 34 | 103.184.45.244,2053,true,SJC,North America,San Jose,172 ms,2217 kB/s 35 | 103.184.45.127,2053,true,SJC,North America,San Jose,229 ms,2207 kB/s 36 | 103.184.45.39,2053,true,SJC,North America,San Jose,171 ms,2202 kB/s 37 | 103.184.45.64,2053,true,SJC,North America,San Jose,205 ms,2184 kB/s 38 | 103.184.45.148,2053,true,SJC,North America,San Jose,185 ms,2164 kB/s 39 | 103.184.45.108,2053,true,SJC,North America,San Jose,224 ms,2028 kB/s 40 | 103.184.45.65,2053,true,SJC,North America,San Jose,197 ms,2027 kB/s 41 | 103.184.45.239,2053,true,SJC,North America,San Jose,169 ms,2021 kB/s 42 | 103.184.45.126,2053,true,SJC,North America,San Jose,222 ms,2011 kB/s 43 | 103.184.45.171,2053,true,SJC,North America,San Jose,184 ms,1991 kB/s 44 | 103.184.45.177,2053,true,SJC,North America,San Jose,163 ms,1951 kB/s 45 | 103.184.45.162,2053,true,SJC,North America,San Jose,171 ms,1939 kB/s 46 | 103.184.45.47,2053,true,SJC,North America,San Jose,179 ms,1936 kB/s 47 | 103.184.45.23,2053,true,SJC,North America,San Jose,172 ms,1933 kB/s 48 | 103.184.45.99,2053,true,SJC,North America,San Jose,222 ms,1925 kB/s 49 | 103.184.45.12,2053,true,SJC,North America,San Jose,163 ms,1919 kB/s 50 | 103.184.45.43,2053,true,SJC,North America,San Jose,179 ms,1913 kB/s 51 | 103.184.45.186,2053,true,SJC,North America,San Jose,197 ms,1902 kB/s 52 | 103.184.45.56,2053,true,SJC,North America,San Jose,185 ms,1897 kB/s 53 | 103.184.45.2,2053,true,SJC,North America,San Jose,156 ms,1888 kB/s 54 | 103.184.45.60,2053,true,SJC,North America,San Jose,204 ms,1882 kB/s 55 | 103.184.45.174,2053,true,SJC,North America,San Jose,185 ms,1871 kB/s 56 | 103.184.45.195,2053,true,SJC,North America,San Jose,191 ms,1866 kB/s 57 | 103.184.45.150,2053,true,SJC,North America,San Jose,203 ms,1865 kB/s 58 | 103.184.45.169,2053,true,SJC,North America,San Jose,178 ms,1860 kB/s 59 | 103.184.45.35,2053,true,SJC,North America,San Jose,158 ms,1848 kB/s 60 | 103.184.45.17,2053,true,SJC,North America,San Jose,173 ms,1844 kB/s 61 | 103.184.45.94,2053,true,SJC,North America,San Jose,229 ms,1837 kB/s 62 | 103.184.45.42,2053,true,SJC,North America,San Jose,188 ms,1827 kB/s 63 | 103.184.45.200,2053,true,SJC,North America,San Jose,152 ms,1822 kB/s 64 | 103.184.45.156,2053,true,SJC,North America,San Jose,186 ms,1818 kB/s 65 | 103.184.45.144,2053,true,SJC,North America,San Jose,203 ms,1810 kB/s 66 | 103.184.45.55,2053,true,SJC,North America,San Jose,165 ms,1804 kB/s 67 | 103.184.45.159,2053,true,SJC,North America,San Jose,191 ms,1802 kB/s 68 | 103.184.45.9,2053,true,SJC,North America,San Jose,162 ms,1777 kB/s 69 | 103.184.45.25,2053,true,SJC,North America,San Jose,176 ms,1765 kB/s 70 | 103.184.45.125,2053,true,SJC,North America,San Jose,214 ms,1742 kB/s 71 | 103.184.45.57,2053,true,SJC,North America,San Jose,181 ms,1736 kB/s 72 | 103.184.45.20,2053,true,SJC,North America,San Jose,160 ms,1735 kB/s 73 | 103.184.45.213,2053,true,SJC,North America,San Jose,156 ms,1734 kB/s 74 | 103.184.45.147,2053,true,SJC,North America,San Jose,172 ms,1732 kB/s 75 | 103.184.45.114,2053,true,SJC,North America,San Jose,223 ms,1728 kB/s 76 | 103.184.45.202,2053,true,SJC,North America,San Jose,159 ms,1728 kB/s 77 | 103.184.45.117,2053,true,SJC,North America,San Jose,213 ms,1726 kB/s 78 | 103.184.45.109,2053,true,SJC,North America,San Jose,214 ms,1721 kB/s 79 | 103.184.45.103,2053,true,SJC,North America,San Jose,183 ms,1721 kB/s 80 | 103.184.45.190,2053,true,SJC,North America,San Jose,204 ms,1718 kB/s 81 | 103.184.45.180,2053,true,SJC,North America,San Jose,190 ms,1717 kB/s 82 | 103.184.45.214,2053,true,SJC,North America,San Jose,172 ms,1707 kB/s 83 | 103.184.45.158,2053,true,SJC,North America,San Jose,203 ms,1692 kB/s 84 | 103.184.45.237,2053,true,SJC,North America,San Jose,169 ms,1692 kB/s 85 | 103.184.45.36,2053,true,SJC,North America,San Jose,156 ms,1677 kB/s 86 | 103.184.45.46,2053,true,SJC,North America,San Jose,175 ms,1668 kB/s 87 | 103.184.45.37,2053,true,SJC,North America,San Jose,157 ms,1660 kB/s 88 | 103.184.45.123,2053,true,SJC,North America,San Jose,223 ms,1659 kB/s 89 | 103.184.45.33,2053,true,SJC,North America,San Jose,168 ms,1650 kB/s 90 | 103.184.45.183,2053,true,SJC,North America,San Jose,205 ms,1647 kB/s 91 | 103.184.45.219,2053,true,SJC,North America,San Jose,161 ms,1646 kB/s 92 | 103.184.45.188,2053,true,SJC,North America,San Jose,183 ms,1630 kB/s 93 | 103.184.45.168,2053,true,SJC,North America,San Jose,197 ms,1629 kB/s 94 | 103.184.45.93,2053,true,SJC,North America,San Jose,222 ms,1601 kB/s 95 | 103.184.45.121,2053,true,SJC,North America,San Jose,215 ms,1594 kB/s 96 | 103.184.45.105,2053,true,SJC,North America,San Jose,204 ms,1590 kB/s 97 | 103.184.45.31,2053,true,SJC,North America,San Jose,164 ms,1582 kB/s 98 | 103.184.45.21,2053,true,SJC,North America,San Jose,166 ms,1571 kB/s 99 | 103.184.45.228,2053,true,SJC,North America,San Jose,170 ms,1566 kB/s 100 | 103.184.45.49,2053,true,SJC,North America,San Jose,196 ms,1564 kB/s 101 | 103.184.45.151,2053,true,SJC,North America,San Jose,197 ms,1554 kB/s 102 | 103.184.45.178,2053,true,SJC,North America,San Jose,178 ms,1545 kB/s 103 | 103.184.45.185,2053,true,SJC,North America,San Jose,203 ms,1541 kB/s 104 | 103.184.45.15,2053,true,SJC,North America,San Jose,160 ms,1541 kB/s 105 | 103.184.45.82,2053,true,SJC,North America,San Jose,205 ms,1538 kB/s 106 | 103.184.45.172,2053,true,SJC,North America,San Jose,186 ms,1510 kB/s 107 | 103.184.45.72,2053,true,SJC,North America,San Jose,190 ms,1504 kB/s 108 | 103.184.45.112,2053,true,SJC,North America,San Jose,212 ms,1504 kB/s 109 | 103.184.45.154,2053,true,SJC,North America,San Jose,173 ms,1493 kB/s 110 | 103.184.45.74,2053,true,SJC,North America,San Jose,197 ms,1492 kB/s 111 | 103.184.45.173,2053,true,SJC,North America,San Jose,196 ms,1491 kB/s 112 | 103.184.45.59,2053,true,SJC,North America,San Jose,203 ms,1487 kB/s 113 | 103.184.45.118,2053,true,SJC,North America,San Jose,213 ms,1471 kB/s 114 | 103.184.45.26,2053,true,SJC,North America,San Jose,167 ms,1468 kB/s 115 | 103.184.45.139,2053,true,SJC,North America,San Jose,205 ms,1467 kB/s 116 | 103.184.45.184,2053,true,SJC,North America,San Jose,197 ms,1465 kB/s 117 | 103.184.45.175,2053,true,SJC,North America,San Jose,196 ms,1465 kB/s 118 | 103.184.45.85,2053,true,SJC,North America,San Jose,222 ms,1465 kB/s 119 | 103.184.45.134,2053,true,SJC,North America,San Jose,203 ms,1461 kB/s 120 | 103.184.45.48,2053,true,SJC,North America,San Jose,191 ms,1448 kB/s 121 | 103.184.45.44,2053,true,SJC,North America,San Jose,178 ms,1446 kB/s 122 | 103.184.45.8,2053,true,SJC,North America,San Jose,175 ms,1444 kB/s 123 | 103.184.45.97,2053,true,SJC,North America,San Jose,223 ms,1429 kB/s 124 | 103.184.45.71,2053,true,SJC,North America,San Jose,186 ms,1426 kB/s 125 | 103.184.45.251,2053,true,SJC,North America,San Jose,169 ms,1420 kB/s 126 | 103.184.45.116,2053,true,SJC,North America,San Jose,212 ms,1411 kB/s 127 | 103.184.45.6,2053,true,SJC,North America,San Jose,173 ms,1383 kB/s 128 | 103.184.45.27,2053,true,SJC,North America,San Jose,172 ms,1370 kB/s 129 | 103.184.45.61,2053,true,SJC,North America,San Jose,205 ms,1354 kB/s 130 | 103.184.45.70,2053,true,SJC,North America,San Jose,164 ms,1351 kB/s 131 | 103.184.45.166,2053,true,SJC,North America,San Jose,196 ms,1344 kB/s 132 | 103.184.45.83,2053,true,SJC,North America,San Jose,213 ms,1319 kB/s 133 | 103.184.45.212,2053,true,SJC,North America,San Jose,161 ms,1297 kB/s 134 | 103.184.45.111,2053,true,SJC,North America,San Jose,222 ms,1266 kB/s 135 | 103.184.45.75,2053,true,SJC,North America,San Jose,222 ms,1253 kB/s 136 | 103.184.45.87,2053,true,SJC,North America,San Jose,214 ms,1251 kB/s 137 | 103.184.45.34,2053,true,SJC,North America,San Jose,178 ms,1249 kB/s 138 | 103.184.45.224,2053,true,SJC,North America,San Jose,166 ms,1241 kB/s 139 | 103.184.45.29,2053,true,SJC,North America,San Jose,162 ms,1241 kB/s 140 | 103.184.45.80,2053,true,SJC,North America,San Jose,203 ms,1241 kB/s 141 | 103.184.45.88,2053,true,SJC,North America,San Jose,202 ms,1208 kB/s 142 | 103.184.45.218,2053,true,SJC,North America,San Jose,164 ms,1198 kB/s 143 | 103.184.45.220,2053,true,SJC,North America,San Jose,153 ms,1186 kB/s 144 | 103.184.45.146,2053,true,SJC,North America,San Jose,175 ms,1169 kB/s 145 | 103.184.45.86,2053,true,SJC,North America,San Jose,222 ms,1145 kB/s 146 | 103.184.45.194,2053,true,SJC,North America,San Jose,191 ms,1116 kB/s 147 | 103.184.45.120,2053,true,SJC,North America,San Jose,223 ms,1111 kB/s 148 | 103.184.45.19,2053,true,SJC,North America,San Jose,162 ms,1098 kB/s 149 | 103.184.45.107,2053,true,SJC,North America,San Jose,222 ms,1096 kB/s 150 | 103.184.45.206,2053,true,SJC,North America,San Jose,164 ms,1093 kB/s 151 | 103.184.45.41,2053,true,SJC,North America,San Jose,188 ms,1093 kB/s 152 | 103.184.45.179,2053,true,SJC,North America,San Jose,187 ms,1075 kB/s 153 | 103.184.45.45,2053,true,SJC,North America,San Jose,183 ms,1073 kB/s 154 | 103.184.45.81,2053,true,SJC,North America,San Jose,214 ms,1064 kB/s 155 | 103.184.45.77,2053,true,SJC,North America,San Jose,229 ms,1064 kB/s 156 | 103.184.45.79,2053,true,SJC,North America,San Jose,223 ms,1048 kB/s 157 | 103.184.45.207,2053,true,SJC,North America,San Jose,159 ms,1047 kB/s 158 | 103.184.45.248,2053,true,SJC,North America,San Jose,172 ms,1038 kB/s 159 | 103.184.45.227,2053,true,SJC,North America,San Jose,162 ms,1029 kB/s 160 | 103.184.45.197,2053,true,SJC,North America,San Jose,196 ms,1020 kB/s 161 | 103.184.45.201,2053,true,SJC,North America,San Jose,162 ms,1006 kB/s 162 | 103.184.45.221,2053,true,SJC,North America,San Jose,154 ms,972 kB/s 163 | 103.184.45.7,2053,true,SJC,North America,San Jose,168 ms,970 kB/s 164 | 103.184.45.242,2053,true,SJC,North America,San Jose,170 ms,970 kB/s 165 | 103.184.45.215,2053,true,SJC,North America,San Jose,165 ms,947 kB/s 166 | 103.184.45.68,2053,true,SJC,North America,San Jose,213 ms,927 kB/s 167 | 103.184.45.51,2053,true,SJC,North America,San Jose,179 ms,918 kB/s 168 | 103.184.45.210,2053,true,SJC,North America,San Jose,160 ms,865 kB/s 169 | 103.184.45.137,2053,true,SJC,North America,San Jose,206 ms,600 kB/s 170 | 103.184.45.69,2053,true,SJC,North America,San Jose,214 ms,453 kB/s 171 | 103.184.45.252,2053,true,SJC,North America,San Jose,170 ms,365 kB/s 172 | 103.184.45.209,2053,true,SJC,North America,San Jose,157 ms,0 kB/s 173 | 103.184.45.216,2053,true,SJC,North America,San Jose,159 ms,0 kB/s 174 | 103.184.45.223,2053,true,SJC,North America,San Jose,164 ms,0 kB/s 175 | 103.184.45.187,2053,true,SJC,North America,San Jose,212 ms,0 kB/s 176 | 103.184.45.225,2053,true,SJC,North America,San Jose,162 ms,0 kB/s 177 | -------------------------------------------------------------------------------- /TestData/4.txt: -------------------------------------------------------------------------------- 1 | 2406:da18:a06:4800:202f:afe8:da42:5822 3666 2 | [2406:da18:a06:4800:202f:afe8:da42:586d] 3 | [2406:da18:a06:4800:202f:afe8:da42:58d]:4422 4 | [2406:da18:a06:4800:202f:afe8:da42:58] 4421 5 | google.com 6 | abc.com:443 7 | connectivity.cloudflareclient.com 2555 8 | 192.168.10.25 9 | 145.239.0.45 5222 10 | 125.255.255.255, 14235 11 | [2406:da18:a06:4800:202f:afe8:d42:57], 443 12 | cloudflare.com, 5212 13 | 2406:da18:a06:4800:202f:afe8:d42:54,444 -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | # 该配置本来就是clash的节点配置 2 | 3 | proxies: 4 | # id 1 5 | - name: vless-ws 6 | server: "" 7 | port: 80 8 | type: vless 9 | uuid: 5be9524f-ed57-4957-9644-00be2a1e8e83 # 修改这里 10 | network: ws 11 | udp: false 12 | tls: false 13 | client-fingerprint: chrome 14 | skip-cert-verify: true 15 | ws-opts: 16 | path: /?ed=2048 # 修改这里 17 | headers: 18 | Host: vless.username.workers.dev # 修改这里 19 | 20 | # id 2 21 | - name: vless-ws-tls 22 | server: "" 23 | port: 443 24 | type: vless 25 | uuid: 0195cf0e-1b62-7ecf-892e-3bc510e47915 # 修改这里 26 | network: ws 27 | udp: false 28 | tls: true # 这个跟前面那个不同 29 | servername: vless.pages.dev # 修改这里 30 | client-fingerprint: chrome 31 | skip-cert-verify: true 32 | ws-opts: 33 | path: /?ed=2048 # 修改这里 34 | headers: 35 | Host: vless.pages.dev # 修改这里 36 | 37 | # id 3:clash核心不支持trojan-ws,只支持trojan-ws-tls 38 | - name: trojan-ws-tls 39 | server: "" 40 | port: 443 41 | type: trojan 42 | password: 0195cf0d-f3f8-72f4-80dc-5385831c8dfa # 修改这里 43 | network: ws 44 | udp: false 45 | sni: trojan.pages.dev # 修改这里 46 | client-fingerprint: chrome 47 | skip-cert-verify: true 48 | ws-opts: 49 | path: / # 修改这里 50 | headers: 51 | Host: trojan.pages.dev # 修改这里 52 | 53 | # id 4 54 | - name: shadowsocks-v2ray_plugin-nTLS 55 | server: "" 56 | port: 80 57 | type: ss 58 | cipher: none 59 | password: none # 这个值,修改无效 60 | udp: false 61 | plugin: v2ray-plugin 62 | plugin-opts: 63 | mode: websocket 64 | # path: /127.0.0.1-443/SSSIG_value # 修改这里-PROXYIP(格式:/IP-端口、/PROXYIP/SSSIG) 65 | path: /127.0.0.1-443 # 修改这里-PROXYIP(格式:/IP-端口、/PROXYIP/SSSIG) 66 | host: ss-v2ray.pages.dev # 修改这里 67 | tls: false # 选择性修改这里,确定是否开启TLS,使用什么端口 68 | mux: false 69 | 70 | # id 5 71 | - name: shadowsocks-v2ray_plugin-TLS 72 | server: "" 73 | port: 443 74 | type: ss 75 | cipher: none 76 | password: none # 这个值,修改无效 77 | udp: false 78 | plugin: v2ray-plugin 79 | plugin-opts: 80 | mode: websocket 81 | path: /127.0.0.1-443 # 修改这里-PROXYIP(格式:/IP-端口、/PROXYIP/SSSIG) 82 | host: ss-v2ray.pages.dev # 修改这里 83 | tls: true # 选择性修改这里,确定是否开启TLS,使用什么端口 84 | mux: false 85 | 86 | # 后面可以继续模仿前面的配置添加 87 | # 注意:同一个脚本,在这里推荐写一个,如果同脚本配置多个节点,使用singbox、clash会导致所有的节点无法使用,需要等待一段时间后,才能使用。 88 | -------------------------------------------------------------------------------- /data/CloudflareST.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juerson/actix-cfwks-subconverter-yaml/6f558249fc481fe026b42b1ec1e41e050da64df8/data/CloudflareST.exe -------------------------------------------------------------------------------- /data/ipv4.txt: -------------------------------------------------------------------------------- 1 | 173.245.48.0/20 2 | 103.21.244.0/22 3 | 103.22.200.0/22 4 | 103.31.4.0/22 5 | 141.101.64.0/18 6 | 108.162.192.0/18 7 | 190.93.240.0/20 8 | 188.114.96.0/20 9 | 197.234.240.0/22 10 | 198.41.128.0/17 11 | 162.158.0.0/15 12 | 104.16.0.0/12 13 | 172.64.0.0/17 14 | 172.64.128.0/18 15 | 172.64.192.0/19 16 | 172.64.224.0/22 17 | 172.64.229.0/24 18 | 172.64.230.0/23 19 | 172.64.232.0/21 20 | 172.64.240.0/21 21 | 172.64.248.0/21 22 | 172.65.0.0/16 23 | 172.66.0.0/16 24 | 172.67.0.0/16 25 | 131.0.72.0/22 -------------------------------------------------------------------------------- /data/使用CloudflareST测试延迟.bat: -------------------------------------------------------------------------------- 1 | CloudflareST.exe -f ipv4.txt -tp 443 -o result.csv -dd -------------------------------------------------------------------------------- /data/相关程序的备份/1_CloudflareST/CloudflareST.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juerson/actix-cfwks-subconverter-yaml/6f558249fc481fe026b42b1ec1e41e050da64df8/data/相关程序的备份/1_CloudflareST/CloudflareST.exe -------------------------------------------------------------------------------- /data/相关程序的备份/1_CloudflareST/ipv4.txt: -------------------------------------------------------------------------------- 1 | 173.245.48.0/20 2 | 103.21.244.0/22 3 | 103.22.200.0/22 4 | 103.31.4.0/22 5 | 141.101.64.0/18 6 | 108.162.192.0/18 7 | 190.93.240.0/20 8 | 188.114.96.0/20 9 | 197.234.240.0/22 10 | 198.41.128.0/17 11 | 162.158.0.0/15 12 | 104.16.0.0/12 13 | 172.64.0.0/17 14 | 172.64.128.0/18 15 | 172.64.192.0/19 16 | 172.64.224.0/22 17 | 172.64.229.0/24 18 | 172.64.230.0/23 19 | 172.64.232.0/21 20 | 172.64.240.0/21 21 | 172.64.248.0/21 22 | 172.65.0.0/16 23 | 172.66.0.0/16 24 | 172.67.0.0/16 25 | 131.0.72.0/22 -------------------------------------------------------------------------------- /data/相关程序的备份/1_CloudflareST/ipv6.txt: -------------------------------------------------------------------------------- 1 | 2400:cb00:2049::/48 2 | 2400:cb00:f00e::/48 3 | 2606:4700::/32 4 | 2606:4700:10::/48 5 | 2606:4700:130::/48 6 | 2606:4700:3000::/48 7 | 2606:4700:3001::/48 8 | 2606:4700:3002::/48 9 | 2606:4700:3003::/48 10 | 2606:4700:3004::/48 11 | 2606:4700:3005::/48 12 | 2606:4700:3006::/48 13 | 2606:4700:3007::/48 14 | 2606:4700:3008::/48 15 | 2606:4700:3009::/48 16 | 2606:4700:3010::/48 17 | 2606:4700:3011::/48 18 | 2606:4700:3012::/48 19 | 2606:4700:3013::/48 20 | 2606:4700:3014::/48 21 | 2606:4700:3015::/48 22 | 2606:4700:3016::/48 23 | 2606:4700:3017::/48 24 | 2606:4700:3018::/48 25 | 2606:4700:3019::/48 26 | 2606:4700:3020::/48 27 | 2606:4700:3021::/48 28 | 2606:4700:3022::/48 29 | 2606:4700:3023::/48 30 | 2606:4700:3024::/48 31 | 2606:4700:3025::/48 32 | 2606:4700:3026::/48 33 | 2606:4700:3027::/48 34 | 2606:4700:3028::/48 35 | 2606:4700:3029::/48 36 | 2606:4700:3030::/48 37 | 2606:4700:3031::/48 38 | 2606:4700:3032::/48 39 | 2606:4700:3033::/48 40 | 2606:4700:3034::/48 41 | 2606:4700:3035::/48 42 | 2606:4700:3036::/48 43 | 2606:4700:3037::/48 44 | 2606:4700:3038::/48 45 | 2606:4700:3039::/48 46 | 2606:4700:a0::/48 47 | 2606:4700:a1::/48 48 | 2606:4700:a8::/48 49 | 2606:4700:a9::/48 50 | 2606:4700:a::/48 51 | 2606:4700:b::/48 52 | 2606:4700:c::/48 53 | 2606:4700:d0::/48 54 | 2606:4700:d1::/48 55 | 2606:4700:d::/48 56 | 2606:4700:e0::/48 57 | 2606:4700:e1::/48 58 | 2606:4700:e2::/48 59 | 2606:4700:e3::/48 60 | 2606:4700:e4::/48 61 | 2606:4700:e5::/48 62 | 2606:4700:e6::/48 63 | 2606:4700:e7::/48 64 | 2606:4700:e::/48 65 | 2606:4700:f1::/48 66 | 2606:4700:f2::/48 67 | 2606:4700:f3::/48 68 | 2606:4700:f4::/48 69 | 2606:4700:f5::/48 70 | 2606:4700:f::/48 71 | 2803:f800:50::/48 72 | 2803:f800:51::/48 73 | 2a06:98c1:3100::/48 74 | 2a06:98c1:3101::/48 75 | 2a06:98c1:3102::/48 76 | 2a06:98c1:3103::/48 77 | 2a06:98c1:3104::/48 78 | 2a06:98c1:3105::/48 79 | 2a06:98c1:3106::/48 80 | 2a06:98c1:3107::/48 81 | 2a06:98c1:3108::/48 82 | 2a06:98c1:3109::/48 83 | 2a06:98c1:310a::/48 84 | 2a06:98c1:310b::/48 85 | 2a06:98c1:310c::/48 86 | 2a06:98c1:310d::/48 87 | 2a06:98c1:310e::/48 88 | 2a06:98c1:310f::/48 89 | 2a06:98c1:3120::/48 90 | 2a06:98c1:3121::/48 91 | 2a06:98c1:3122::/48 92 | 2a06:98c1:3123::/48 93 | 2a06:98c1:3200::/48 94 | 2a06:98c1:50::/48 95 | 2a06:98c1:51::/48 96 | 2a06:98c1:54::/48 97 | 2a06:98c1:58::/48 -------------------------------------------------------------------------------- /data/相关程序的备份/1_CloudflareST/使用CloudflareST测试延迟.bat: -------------------------------------------------------------------------------- 1 | CloudflareST.exe -f ipv4.txt -tp 443 -o result.csv -dd -------------------------------------------------------------------------------- /data/相关程序的备份/2_ipspeedtest/ip.csv: -------------------------------------------------------------------------------- 1 | IP地址,端口,TLS,数据中心,地区,城市,网络延迟,下载速度 2 | 103.184.45.253,2053,true,SJC,North America,San Jose,169 ms,4291 kB/s 3 | 103.184.45.217,2053,true,SJC,North America,San Jose,163 ms,3670 kB/s 4 | 103.184.45.164,2053,true,SJC,North America,San Jose,190 ms,3591 kB/s 5 | 103.184.45.204,2053,true,SJC,North America,San Jose,160 ms,3019 kB/s 6 | 103.184.45.240,2053,true,SJC,North America,San Jose,168 ms,2981 kB/s 7 | 103.184.45.226,2053,true,SJC,North America,San Jose,162 ms,2961 kB/s 8 | 103.184.45.155,2053,true,SJC,North America,San Jose,175 ms,2830 kB/s 9 | 103.184.45.32,2053,true,SJC,North America,San Jose,165 ms,2811 kB/s 10 | 103.184.45.254,2053,true,SJC,North America,San Jose,178 ms,2776 kB/s 11 | 103.184.45.189,2053,true,SJC,North America,San Jose,197 ms,2664 kB/s 12 | 103.184.45.73,2053,true,SJC,North America,San Jose,196 ms,2604 kB/s 13 | 103.184.45.98,2053,true,SJC,North America,San Jose,223 ms,2595 kB/s 14 | 103.184.45.5,2053,true,SJC,North America,San Jose,155 ms,2588 kB/s 15 | 103.184.45.63,2053,true,SJC,North America,San Jose,203 ms,2517 kB/s 16 | 103.184.45.243,2053,true,SJC,North America,San Jose,170 ms,2497 kB/s 17 | 103.184.45.193,2053,true,SJC,North America,San Jose,187 ms,2481 kB/s 18 | 103.184.45.196,2053,true,SJC,North America,San Jose,197 ms,2475 kB/s 19 | 103.184.45.192,2053,true,SJC,North America,San Jose,214 ms,2462 kB/s 20 | 103.184.45.58,2053,true,SJC,North America,San Jose,191 ms,2454 kB/s 21 | 103.184.45.106,2053,true,SJC,North America,San Jose,213 ms,2453 kB/s 22 | 103.184.45.62,2053,true,SJC,North America,San Jose,212 ms,2409 kB/s 23 | 103.184.45.78,2053,true,SJC,North America,San Jose,214 ms,2396 kB/s 24 | 103.184.45.54,2053,true,SJC,North America,San Jose,162 ms,2382 kB/s 25 | 103.184.45.66,2053,true,SJC,North America,San Jose,204 ms,2365 kB/s 26 | 103.184.45.181,2053,true,SJC,North America,San Jose,191 ms,2359 kB/s 27 | 103.184.45.247,2053,true,SJC,North America,San Jose,173 ms,2321 kB/s 28 | 103.184.45.161,2053,true,SJC,North America,San Jose,170 ms,2321 kB/s 29 | 103.184.45.28,2053,true,SJC,North America,San Jose,167 ms,2281 kB/s 30 | 103.184.45.92,2053,true,SJC,North America,San Jose,222 ms,2268 kB/s 31 | 103.184.45.115,2053,true,SJC,North America,San Jose,213 ms,2259 kB/s 32 | 103.184.45.249,2053,true,SJC,North America,San Jose,167 ms,2248 kB/s 33 | 103.184.45.124,2053,true,SJC,North America,San Jose,213 ms,2220 kB/s 34 | 103.184.45.244,2053,true,SJC,North America,San Jose,172 ms,2217 kB/s 35 | 103.184.45.127,2053,true,SJC,North America,San Jose,229 ms,2207 kB/s 36 | 103.184.45.39,2053,true,SJC,North America,San Jose,171 ms,2202 kB/s 37 | 103.184.45.64,2053,true,SJC,North America,San Jose,205 ms,2184 kB/s 38 | 103.184.45.148,2053,true,SJC,North America,San Jose,185 ms,2164 kB/s 39 | 103.184.45.108,2053,true,SJC,North America,San Jose,224 ms,2028 kB/s 40 | 103.184.45.65,2053,true,SJC,North America,San Jose,197 ms,2027 kB/s 41 | 103.184.45.239,2053,true,SJC,North America,San Jose,169 ms,2021 kB/s 42 | 103.184.45.126,2053,true,SJC,North America,San Jose,222 ms,2011 kB/s 43 | 103.184.45.171,2053,true,SJC,North America,San Jose,184 ms,1991 kB/s 44 | 103.184.45.177,2053,true,SJC,North America,San Jose,163 ms,1951 kB/s 45 | 103.184.45.162,2053,true,SJC,North America,San Jose,171 ms,1939 kB/s 46 | 103.184.45.47,2053,true,SJC,North America,San Jose,179 ms,1936 kB/s 47 | 103.184.45.23,2053,true,SJC,North America,San Jose,172 ms,1933 kB/s 48 | 103.184.45.99,2053,true,SJC,North America,San Jose,222 ms,1925 kB/s 49 | 103.184.45.12,2053,true,SJC,North America,San Jose,163 ms,1919 kB/s 50 | 103.184.45.43,2053,true,SJC,North America,San Jose,179 ms,1913 kB/s 51 | 103.184.45.186,2053,true,SJC,North America,San Jose,197 ms,1902 kB/s 52 | 103.184.45.56,2053,true,SJC,North America,San Jose,185 ms,1897 kB/s 53 | 103.184.45.2,2053,true,SJC,North America,San Jose,156 ms,1888 kB/s 54 | 103.184.45.60,2053,true,SJC,North America,San Jose,204 ms,1882 kB/s 55 | 103.184.45.174,2053,true,SJC,North America,San Jose,185 ms,1871 kB/s 56 | 103.184.45.195,2053,true,SJC,North America,San Jose,191 ms,1866 kB/s 57 | 103.184.45.150,2053,true,SJC,North America,San Jose,203 ms,1865 kB/s 58 | 103.184.45.169,2053,true,SJC,North America,San Jose,178 ms,1860 kB/s 59 | 103.184.45.35,2053,true,SJC,North America,San Jose,158 ms,1848 kB/s 60 | 103.184.45.17,2053,true,SJC,North America,San Jose,173 ms,1844 kB/s 61 | 103.184.45.94,2053,true,SJC,North America,San Jose,229 ms,1837 kB/s 62 | 103.184.45.42,2053,true,SJC,North America,San Jose,188 ms,1827 kB/s 63 | 103.184.45.200,2053,true,SJC,North America,San Jose,152 ms,1822 kB/s 64 | 103.184.45.156,2053,true,SJC,North America,San Jose,186 ms,1818 kB/s 65 | 103.184.45.144,2053,true,SJC,North America,San Jose,203 ms,1810 kB/s 66 | 103.184.45.55,2053,true,SJC,North America,San Jose,165 ms,1804 kB/s 67 | 103.184.45.159,2053,true,SJC,North America,San Jose,191 ms,1802 kB/s 68 | 103.184.45.9,2053,true,SJC,North America,San Jose,162 ms,1777 kB/s 69 | 103.184.45.25,2053,true,SJC,North America,San Jose,176 ms,1765 kB/s 70 | 103.184.45.125,2053,true,SJC,North America,San Jose,214 ms,1742 kB/s 71 | 103.184.45.57,2053,true,SJC,North America,San Jose,181 ms,1736 kB/s 72 | 103.184.45.20,2053,true,SJC,North America,San Jose,160 ms,1735 kB/s 73 | 103.184.45.213,2053,true,SJC,North America,San Jose,156 ms,1734 kB/s 74 | 103.184.45.147,2053,true,SJC,North America,San Jose,172 ms,1732 kB/s 75 | 103.184.45.114,2053,true,SJC,North America,San Jose,223 ms,1728 kB/s 76 | 103.184.45.202,2053,true,SJC,North America,San Jose,159 ms,1728 kB/s 77 | 103.184.45.117,2053,true,SJC,North America,San Jose,213 ms,1726 kB/s 78 | 103.184.45.109,2053,true,SJC,North America,San Jose,214 ms,1721 kB/s 79 | 103.184.45.103,2053,true,SJC,North America,San Jose,183 ms,1721 kB/s 80 | 103.184.45.190,2053,true,SJC,North America,San Jose,204 ms,1718 kB/s 81 | 103.184.45.180,2053,true,SJC,North America,San Jose,190 ms,1717 kB/s 82 | 103.184.45.214,2053,true,SJC,North America,San Jose,172 ms,1707 kB/s 83 | 103.184.45.158,2053,true,SJC,North America,San Jose,203 ms,1692 kB/s 84 | 103.184.45.237,2053,true,SJC,North America,San Jose,169 ms,1692 kB/s 85 | 103.184.45.36,2053,true,SJC,North America,San Jose,156 ms,1677 kB/s 86 | 103.184.45.46,2053,true,SJC,North America,San Jose,175 ms,1668 kB/s 87 | 103.184.45.37,2053,true,SJC,North America,San Jose,157 ms,1660 kB/s 88 | 103.184.45.123,2053,true,SJC,North America,San Jose,223 ms,1659 kB/s 89 | 103.184.45.33,2053,true,SJC,North America,San Jose,168 ms,1650 kB/s 90 | 103.184.45.183,2053,true,SJC,North America,San Jose,205 ms,1647 kB/s 91 | 103.184.45.219,2053,true,SJC,North America,San Jose,161 ms,1646 kB/s 92 | 103.184.45.188,2053,true,SJC,North America,San Jose,183 ms,1630 kB/s 93 | 103.184.45.168,2053,true,SJC,North America,San Jose,197 ms,1629 kB/s 94 | 103.184.45.93,2053,true,SJC,North America,San Jose,222 ms,1601 kB/s 95 | 103.184.45.121,2053,true,SJC,North America,San Jose,215 ms,1594 kB/s 96 | 103.184.45.105,2053,true,SJC,North America,San Jose,204 ms,1590 kB/s 97 | 103.184.45.31,2053,true,SJC,North America,San Jose,164 ms,1582 kB/s 98 | 103.184.45.21,2053,true,SJC,North America,San Jose,166 ms,1571 kB/s 99 | 103.184.45.228,2053,true,SJC,North America,San Jose,170 ms,1566 kB/s 100 | 103.184.45.49,2053,true,SJC,North America,San Jose,196 ms,1564 kB/s 101 | 103.184.45.151,2053,true,SJC,North America,San Jose,197 ms,1554 kB/s 102 | 103.184.45.178,2053,true,SJC,North America,San Jose,178 ms,1545 kB/s 103 | 103.184.45.185,2053,true,SJC,North America,San Jose,203 ms,1541 kB/s 104 | 103.184.45.15,2053,true,SJC,North America,San Jose,160 ms,1541 kB/s 105 | 103.184.45.82,2053,true,SJC,North America,San Jose,205 ms,1538 kB/s 106 | 103.184.45.172,2053,true,SJC,North America,San Jose,186 ms,1510 kB/s 107 | 103.184.45.72,2053,true,SJC,North America,San Jose,190 ms,1504 kB/s 108 | 103.184.45.112,2053,true,SJC,North America,San Jose,212 ms,1504 kB/s 109 | 103.184.45.154,2053,true,SJC,North America,San Jose,173 ms,1493 kB/s 110 | 103.184.45.74,2053,true,SJC,North America,San Jose,197 ms,1492 kB/s 111 | 103.184.45.173,2053,true,SJC,North America,San Jose,196 ms,1491 kB/s 112 | 103.184.45.59,2053,true,SJC,North America,San Jose,203 ms,1487 kB/s 113 | 103.184.45.118,2053,true,SJC,North America,San Jose,213 ms,1471 kB/s 114 | 103.184.45.26,2053,true,SJC,North America,San Jose,167 ms,1468 kB/s 115 | 103.184.45.139,2053,true,SJC,North America,San Jose,205 ms,1467 kB/s 116 | 103.184.45.184,2053,true,SJC,North America,San Jose,197 ms,1465 kB/s 117 | 103.184.45.175,2053,true,SJC,North America,San Jose,196 ms,1465 kB/s 118 | 103.184.45.85,2053,true,SJC,North America,San Jose,222 ms,1465 kB/s 119 | 103.184.45.134,2053,true,SJC,North America,San Jose,203 ms,1461 kB/s 120 | 103.184.45.48,2053,true,SJC,North America,San Jose,191 ms,1448 kB/s 121 | 103.184.45.44,2053,true,SJC,North America,San Jose,178 ms,1446 kB/s 122 | 103.184.45.8,2053,true,SJC,North America,San Jose,175 ms,1444 kB/s 123 | 103.184.45.97,2053,true,SJC,North America,San Jose,223 ms,1429 kB/s 124 | 103.184.45.71,2053,true,SJC,North America,San Jose,186 ms,1426 kB/s 125 | 103.184.45.251,2053,true,SJC,North America,San Jose,169 ms,1420 kB/s 126 | 103.184.45.116,2053,true,SJC,North America,San Jose,212 ms,1411 kB/s 127 | 103.184.45.6,2053,true,SJC,North America,San Jose,173 ms,1383 kB/s 128 | 103.184.45.27,2053,true,SJC,North America,San Jose,172 ms,1370 kB/s 129 | 103.184.45.61,2053,true,SJC,North America,San Jose,205 ms,1354 kB/s 130 | 103.184.45.70,2053,true,SJC,North America,San Jose,164 ms,1351 kB/s 131 | 103.184.45.166,2053,true,SJC,North America,San Jose,196 ms,1344 kB/s 132 | 103.184.45.83,2053,true,SJC,North America,San Jose,213 ms,1319 kB/s 133 | 103.184.45.212,2053,true,SJC,North America,San Jose,161 ms,1297 kB/s 134 | 103.184.45.111,2053,true,SJC,North America,San Jose,222 ms,1266 kB/s 135 | 103.184.45.75,2053,true,SJC,North America,San Jose,222 ms,1253 kB/s 136 | 103.184.45.87,2053,true,SJC,North America,San Jose,214 ms,1251 kB/s 137 | 103.184.45.34,2053,true,SJC,North America,San Jose,178 ms,1249 kB/s 138 | 103.184.45.224,2053,true,SJC,North America,San Jose,166 ms,1241 kB/s 139 | 103.184.45.29,2053,true,SJC,North America,San Jose,162 ms,1241 kB/s 140 | 103.184.45.80,2053,true,SJC,North America,San Jose,203 ms,1241 kB/s 141 | 103.184.45.88,2053,true,SJC,North America,San Jose,202 ms,1208 kB/s 142 | 103.184.45.218,2053,true,SJC,North America,San Jose,164 ms,1198 kB/s 143 | 103.184.45.220,2053,true,SJC,North America,San Jose,153 ms,1186 kB/s 144 | 103.184.45.146,2053,true,SJC,North America,San Jose,175 ms,1169 kB/s 145 | 103.184.45.86,2053,true,SJC,North America,San Jose,222 ms,1145 kB/s 146 | 103.184.45.194,2053,true,SJC,North America,San Jose,191 ms,1116 kB/s 147 | 103.184.45.120,2053,true,SJC,North America,San Jose,223 ms,1111 kB/s 148 | 103.184.45.19,2053,true,SJC,North America,San Jose,162 ms,1098 kB/s 149 | 103.184.45.107,2053,true,SJC,North America,San Jose,222 ms,1096 kB/s 150 | 103.184.45.206,2053,true,SJC,North America,San Jose,164 ms,1093 kB/s 151 | 103.184.45.41,2053,true,SJC,North America,San Jose,188 ms,1093 kB/s 152 | 103.184.45.179,2053,true,SJC,North America,San Jose,187 ms,1075 kB/s 153 | 103.184.45.45,2053,true,SJC,North America,San Jose,183 ms,1073 kB/s 154 | 103.184.45.81,2053,true,SJC,North America,San Jose,214 ms,1064 kB/s 155 | 103.184.45.77,2053,true,SJC,North America,San Jose,229 ms,1064 kB/s 156 | 103.184.45.79,2053,true,SJC,North America,San Jose,223 ms,1048 kB/s 157 | 103.184.45.207,2053,true,SJC,North America,San Jose,159 ms,1047 kB/s 158 | 103.184.45.248,2053,true,SJC,North America,San Jose,172 ms,1038 kB/s 159 | 103.184.45.227,2053,true,SJC,North America,San Jose,162 ms,1029 kB/s 160 | 103.184.45.197,2053,true,SJC,North America,San Jose,196 ms,1020 kB/s 161 | 103.184.45.201,2053,true,SJC,North America,San Jose,162 ms,1006 kB/s 162 | 103.184.45.221,2053,true,SJC,North America,San Jose,154 ms,972 kB/s 163 | 103.184.45.7,2053,true,SJC,North America,San Jose,168 ms,970 kB/s 164 | 103.184.45.242,2053,true,SJC,North America,San Jose,170 ms,970 kB/s 165 | 103.184.45.215,2053,true,SJC,North America,San Jose,165 ms,947 kB/s 166 | 103.184.45.68,2053,true,SJC,North America,San Jose,213 ms,927 kB/s 167 | 103.184.45.51,2053,true,SJC,North America,San Jose,179 ms,918 kB/s 168 | 103.184.45.210,2053,true,SJC,North America,San Jose,160 ms,865 kB/s 169 | 103.184.45.137,2053,true,SJC,North America,San Jose,206 ms,600 kB/s 170 | 103.184.45.69,2053,true,SJC,North America,San Jose,214 ms,453 kB/s 171 | 103.184.45.252,2053,true,SJC,North America,San Jose,170 ms,365 kB/s 172 | 103.184.45.209,2053,true,SJC,North America,San Jose,157 ms,0 kB/s 173 | 103.184.45.216,2053,true,SJC,North America,San Jose,159 ms,0 kB/s 174 | 103.184.45.223,2053,true,SJC,North America,San Jose,164 ms,0 kB/s 175 | 103.184.45.187,2053,true,SJC,North America,San Jose,212 ms,0 kB/s 176 | 103.184.45.225,2053,true,SJC,North America,San Jose,162 ms,0 kB/s 177 | -------------------------------------------------------------------------------- /data/相关程序的备份/2_ipspeedtest/ip.txt: -------------------------------------------------------------------------------- 1 | 103.184.45.0/24 -------------------------------------------------------------------------------- /data/相关程序的备份/2_ipspeedtest/ipspeedtest.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juerson/actix-cfwks-subconverter-yaml/6f558249fc481fe026b42b1ec1e41e050da64df8/data/相关程序的备份/2_ipspeedtest/ipspeedtest.exe -------------------------------------------------------------------------------- /data/相关程序的备份/2_ipspeedtest/locations.json: -------------------------------------------------------------------------------- 1 | [{"iata":"TIA","lat":41.4146995544,"lon":19.7206001282,"cca2":"AL","region":"Europe","city":"Tirana"},{"iata":"ALG","lat":36.6910018921,"lon":3.2154099941,"cca2":"DZ","region":"Africa","city":"Algiers"},{"iata":"AAE","lat":36.85596,"lon":7.79207,"cca2":"DZ","region":"Africa","city":"Annaba"},{"iata":"ORN","lat":35.6911,"lon":-0.6416,"cca2":"DZ","region":"Africa","city":"Oran"},{"iata":"LAD","lat":-8.8583698273,"lon":13.2312002182,"cca2":"AO","region":"Africa","city":"Luanda"},{"iata":"EZE","lat":-34.8222,"lon":-58.5358,"cca2":"AR","region":"South America","city":"Buenos Aires"},{"iata":"COR","lat":-31.31,"lon":-64.208333,"cca2":"AR","region":"South America","city":"Córdoba"},{"iata":"NQN","lat":-38.9490013123,"lon":-68.1557006836,"cca2":"AR","region":"South America","city":"Neuquen"},{"iata":"EVN","lat":40.1473007202,"lon":44.3959007263,"cca2":"AM","region":"Middle East","city":"Yerevan"},{"iata":"ADL","lat":-34.9431729,"lon":138.5335637,"cca2":"AU","region":"Oceania","city":"Adelaide"},{"iata":"BNE","lat":-27.3841991425,"lon":153.117004394,"cca2":"AU","region":"Oceania","city":"Brisbane"},{"iata":"CBR","lat":-35.3069000244,"lon":149.1950073242,"cca2":"AU","region":"Oceania","city":"Canberra"},{"iata":"MEL","lat":-37.6733016968,"lon":144.843002319,"cca2":"AU","region":"Oceania","city":"Melbourne"},{"iata":"PER","lat":-31.9402999878,"lon":115.967002869,"cca2":"AU","region":"Oceania","city":"Perth"},{"iata":"SYD","lat":-33.9460983276,"lon":151.177001953,"cca2":"AU","region":"Oceania","city":"Sydney"},{"iata":"VIE","lat":48.1102981567,"lon":16.5697002411,"cca2":"AT","region":"Europe","city":"Vienna"},{"iata":"LLK","lat":38.7463989258,"lon":48.8180007935,"cca2":"AZ","region":"Middle East","city":"Astara"},{"iata":"GYD","lat":40.4674987793,"lon":50.0466995239,"cca2":"AZ","region":"Middle East","city":"Baku"},{"iata":"BAH","lat":26.2707996368,"lon":50.6335983276,"cca2":"BH","region":"Middle East","city":"Manama"},{"iata":"CGP","lat":22.2495995,"lon":91.8133011,"cca2":"BD","region":"Asia Pacific","city":"Chittagong"},{"iata":"DAC","lat":23.843347,"lon":90.397783,"cca2":"BD","region":"Asia Pacific","city":"Dhaka"},{"iata":"JSR","lat":23.1837997437,"lon":89.1607971191,"cca2":"BD","region":"Asia Pacific","city":"Jashore"},{"iata":"BGI","lat":13.103562,"lon":-59.603226,"cca2":"BB","region":"North America","city":"Bridgetown"},{"iata":"MSQ","lat":53.9006,"lon":27.599,"cca2":"BY","region":"Europe","city":"Minsk"},{"iata":"BRU","lat":50.9014015198,"lon":4.4844398499,"cca2":"BE","region":"Europe","city":"Brussels"},{"iata":"PBH","lat":27.4712,"lon":89.6339,"cca2":"BT","region":"Asia Pacific","city":"Thimphu"},{"iata":"LPB","lat":-16.4897,"lon":-68.1193,"cca2":"BO","region":"South America","city":"La Paz"},{"iata":"GBE","lat":-24.6282,"lon":25.9231,"cca2":"BW","region":"Africa","city":"Gaborone"},{"iata":"QWJ","lat":-22.738,"lon":-47.334,"cca2":"BR","region":"South America","city":"Americana"},{"iata":"BEL","lat":-1.4563,"lon":-48.5013,"cca2":"BR","region":"South America","city":"Belém"},{"iata":"CNF","lat":-19.624444,"lon":-43.971944,"cca2":"BR","region":"South America","city":"Belo Horizonte"},{"iata":"BNU","lat":-26.89245,"lon":-49.07696,"cca2":"BR","region":"South America","city":"Blumenau"},{"iata":"BSB","lat":-15.79824,"lon":-47.90859,"cca2":"BR","region":"South America","city":"Brasilia"},{"iata":"CFC","lat":-26.7762,"lon":-51.0125,"cca2":"BR","region":"South America","city":"Cacador"},{"iata":"VCP","lat":-22.90662,"lon":-47.08576,"cca2":"BR","region":"South America","city":"Campinas"},{"iata":"CAW","lat":-21.698299408,"lon":-41.301700592,"cca2":"BR","region":"South America","city":"Campos dos Goytacazes"},{"iata":"XAP","lat":-27.1341991425,"lon":-52.6566009521,"cca2":"BR","region":"South America","city":"Chapeco"},{"iata":"CGB","lat":-15.59611,"lon":-56.09667,"cca2":"BR","region":"South America","city":"Cuiaba"},{"iata":"CWB","lat":-25.5284996033,"lon":-49.1758003235,"cca2":"BR","region":"South America","city":"Curitiba"},{"iata":"FLN","lat":-27.6702785492,"lon":-48.5525016785,"cca2":"BR","region":"South America","city":"Florianopolis"},{"iata":"FOR","lat":-3.7762799263,"lon":-38.5326004028,"cca2":"BR","region":"South America","city":"Fortaleza"},{"iata":"GYN","lat":-16.69727,"lon":-49.26851,"cca2":"BR","region":"South America","city":"Goiania"},{"iata":"ITJ","lat":-27.6116676331,"lon":-48.6727790833,"cca2":"BR","region":"South America","city":"Itajai"},{"iata":"JOI","lat":-26.304408,"lon":-48.846383,"cca2":"BR","region":"South America","city":"Joinville"},{"iata":"JDO","lat":-7.2242,"lon":-39.313,"cca2":"BR","region":"South America","city":"Juazeiro do Norte"},{"iata":"MAO","lat":-3.11286,"lon":-60.01949,"cca2":"BR","region":"South America","city":"Manaus"},{"iata":"PMW","lat":-10.2915000916,"lon":-48.3569984436,"cca2":"BR","region":"South America","city":"Palmas"},{"iata":"POA","lat":-29.9944000244,"lon":-51.1713981628,"cca2":"BR","region":"South America","city":"Porto Alegre"},{"iata":"REC","lat":-8.1264896393,"lon":-34.9235992432,"cca2":"BR","region":"South America","city":"Recife"},{"iata":"RAO","lat":-21.1363887787,"lon":-47.7766685486,"cca2":"BR","region":"South America","city":"Ribeirao Preto"},{"iata":"GIG","lat":-22.8099994659,"lon":-43.2505569458,"cca2":"BR","region":"South America","city":"Rio de Janeiro"},{"iata":"SSA","lat":-12.9086112976,"lon":-38.3224983215,"cca2":"BR","region":"South America","city":"Salvador"},{"iata":"SJP","lat":-20.807157,"lon":-49.378994,"cca2":"BR","region":"South America","city":"São José do Rio Preto"},{"iata":"SJK","lat":-23.1791,"lon":-45.8872,"cca2":"BR","region":"South America","city":"São José dos Campos"},{"iata":"GRU","lat":-23.4355564117,"lon":-46.4730567932,"cca2":"BR","region":"South America","city":"São Paulo"},{"iata":"SOD","lat":-23.54389,"lon":-46.63445,"cca2":"BR","region":"South America","city":"Sorocaba"},{"iata":"NVT","lat":-26.8251,"lon":-49.2695,"cca2":"BR","region":"South America","city":"Timbo"},{"iata":"UDI","lat":-18.8836116791,"lon":-48.225276947,"cca2":"BR","region":"South America","city":"Uberlandia"},{"iata":"VIX","lat":-20.64871,"lon":-41.90857,"cca2":"BR","region":"South America","city":"Vitoria"},{"iata":"BWN","lat":4.903052,"lon":114.939819,"cca2":"BN","region":"Asia Pacific","city":"Bandar Seri Begawan"},{"iata":"SOF","lat":42.6966934204,"lon":23.4114360809,"cca2":"BG","region":"Europe","city":"Sofia"},{"iata":"OUA","lat":12.3531999588,"lon":-1.5124200583,"cca2":"BF","region":"Africa","city":"Ouagadougou"},{"iata":"PNH","lat":11.5466003418,"lon":104.84400177,"cca2":"KH","region":"Asia Pacific","city":"Phnom Penh"},{"iata":"YYC","lat":51.113899231,"lon":-114.019996643,"cca2":"CA","region":"North America","city":"Calgary"},{"iata":"YVR","lat":49.193901062,"lon":-123.183998108,"cca2":"CA","region":"North America","city":"Vancouver"},{"iata":"YWG","lat":49.9099998474,"lon":-97.2398986816,"cca2":"CA","region":"North America","city":"Winnipeg"},{"iata":"YHZ","lat":44.64601,"lon":-63.66844,"cca2":"CA","region":"North America","city":"Halifax"},{"iata":"YOW","lat":45.3224983215,"lon":-75.6691970825,"cca2":"CA","region":"North America","city":"Ottawa"},{"iata":"YYZ","lat":43.6772003174,"lon":-79.6305999756,"cca2":"CA","region":"North America","city":"Toronto"},{"iata":"YUL","lat":45.4706001282,"lon":-73.7407989502,"cca2":"CA","region":"North America","city":"Montréal"},{"iata":"YXE","lat":52.1707992554,"lon":-106.699996948,"cca2":"CA","region":"North America","city":"Saskatoon"},{"iata":"ARI","lat":-18.348611,"lon":-70.338889,"cca2":"CL","region":"South America","city":"Arica"},{"iata":"SCL","lat":-33.3930015564,"lon":-70.7857971191,"cca2":"CL","region":"South America","city":"Santiago"},{"iata":"BAQ","lat":10.8896,"lon":-74.7808,"cca2":"CO","region":"South America","city":"Barranquilla"},{"iata":"BOG","lat":4.70159,"lon":-74.1469,"cca2":"CO","region":"South America","city":"Bogotá"},{"iata":"MDE","lat":6.16454,"lon":-75.4231,"cca2":"CO","region":"South America","city":"Medellín"},{"iata":"FIH","lat":-4.3857498169,"lon":15.4446001053,"cca2":"CD","region":"Africa","city":"Kinshasa"},{"iata":"SJO","lat":9.9938602448,"lon":-84.2088012695,"cca2":"CR","region":"South America","city":"San José"},{"iata":"ZAG","lat":45.7429008484,"lon":16.0687999725,"cca2":"HR","region":"Europe","city":"Zagreb"},{"iata":"CUR","lat":12.1888999939,"lon":-68.9598007202,"cca2":"CW","region":"North America","city":"Willemstad"},{"iata":"LCA","lat":34.8750991821,"lon":33.6249008179,"cca2":"CY","region":"Europe","city":"Nicosia"},{"iata":"PRG","lat":50.1007995605,"lon":14.2600002289,"cca2":"CZ","region":"Europe","city":"Prague"},{"iata":"CPH","lat":55.6179008484,"lon":12.6560001373,"cca2":"DK","region":"Europe","city":"Copenhagen"},{"iata":"JIB","lat":11.5473003387,"lon":43.1595001221,"cca2":"DJ","region":"Africa","city":"Djibouti"},{"iata":"STI","lat":19.4060993195,"lon":-70.6046981812,"cca2":"DO","region":"North America","city":"Santiago de los Caballeros"},{"iata":"SDQ","lat":18.4297008514,"lon":-69.6688995361,"cca2":"DO","region":"North America","city":"Santo Domingo"},{"iata":"GYE","lat":-2.1894,"lon":-79.8891,"cca2":"EC","region":"South America","city":"Guayaquil"},{"iata":"UIO","lat":-0.1291666667,"lon":-78.3575,"cca2":"EC","region":"South America","city":"Quito"},{"iata":"CAI","lat":30.1219005585,"lon":31.4055995941,"cca2":"EG","region":"Africa","city":"Cairo"},{"iata":"TLL","lat":59.4132995605,"lon":24.8327999115,"cca2":"EE","region":"Europe","city":"Tallinn"},{"iata":"SUV","lat":-18.11319,"lon":178.43859,"cca2":"FJ","region":"Oceania","city":"Suva"},{"iata":"HEL","lat":60.317199707,"lon":24.963300705,"cca2":"FI","region":"Europe","city":"Helsinki"},{"iata":"BOD","lat":44.82946,"lon":-0.58355,"cca2":"FR","region":"Europe","city":"Bordeaux"},{"iata":"LYS","lat":45.7263,"lon":5.0908,"cca2":"FR","region":"Europe","city":"Lyon"},{"iata":"MRS","lat":43.439271922,"lon":5.2214241028,"cca2":"FR","region":"Europe","city":"Marseille"},{"iata":"CDG","lat":49.0127983093,"lon":2.5499999523,"cca2":"FR","region":"Europe","city":"Paris"},{"iata":"PPT","lat":-17.5536994934,"lon":-149.606994629,"cca2":"PF","region":"Oceania","city":"Tahiti"},{"iata":"TBS","lat":41.6692008972,"lon":44.95470047,"cca2":"GE","region":"Europe","city":"Tbilisi"},{"iata":"TXL","lat":52.5597000122,"lon":13.2876996994,"cca2":"DE","region":"Europe","city":"Berlin"},{"iata":"DUS","lat":51.2895011902,"lon":6.7667798996,"cca2":"DE","region":"Europe","city":"Düsseldorf"},{"iata":"FRA","lat":50.0264015198,"lon":8.543129921,"cca2":"DE","region":"Europe","city":"Frankfurt"},{"iata":"HAM","lat":53.6304016113,"lon":9.9882297516,"cca2":"DE","region":"Europe","city":"Hamburg"},{"iata":"MUC","lat":48.3538017273,"lon":11.7861003876,"cca2":"DE","region":"Europe","city":"Munich"},{"iata":"STR","lat":48.783333,"lon":9.183333,"cca2":"DE","region":"Europe","city":"Stuttgart"},{"iata":"ACC","lat":5.614818,"lon":-0.205874,"cca2":"GH","region":"Africa","city":"Accra"},{"iata":"ATH","lat":37.9364013672,"lon":23.9444999695,"cca2":"GR","region":"Europe","city":"Athens"},{"iata":"SKG","lat":40.5196990967,"lon":22.9708995819,"cca2":"GR","region":"Europe","city":"Thessaloniki"},{"iata":"GND","lat":12.007116,"lon":-61.7882288,"cca2":"GD","region":"South America","city":"St. George's"},{"iata":"GUM","lat":13.4834003448,"lon":144.796005249,"cca2":"GU","region":"Asia Pacific","city":"Hagatna"},{"iata":"GUA","lat":14.5832996368,"lon":-90.5274963379,"cca2":"GT","region":"North America","city":"Guatemala City"},{"iata":"GEO","lat":6.825648,"lon":-58.163756,"cca2":"GY","region":"South America","city":"Georgetown"},{"iata":"TGU","lat":14.0608,"lon":-87.2172,"cca2":"HN","region":"South America","city":"Tegucigalpa"},{"iata":"HKG","lat":22.3089008331,"lon":113.915000916,"cca2":"HK","region":"Asia Pacific","city":"Hong Kong"},{"iata":"BUD","lat":47.4369010925,"lon":19.2555999756,"cca2":"HU","region":"Europe","city":"Budapest"},{"iata":"KEF","lat":63.9850006104,"lon":-22.6056003571,"cca2":"IS","region":"Europe","city":"Reykjavík"},{"iata":"AMD","lat":23.0225,"lon":72.5714,"cca2":"IN","region":"Asia Pacific","city":"Ahmedabad"},{"iata":"BLR","lat":13.7835719,"lon":76.6165937,"cca2":"IN","region":"Asia Pacific","city":"Bangalore"},{"iata":"BBI","lat":20.2961,"lon":85.8245,"cca2":"IN","region":"Asia Pacific","city":"Bhubaneswar"},{"iata":"IXC","lat":30.673500061,"lon":76.7884979248,"cca2":"IN","region":"Asia Pacific","city":"Chandigarh"},{"iata":"MAA","lat":12.9900054932,"lon":80.1692962646,"cca2":"IN","region":"Asia Pacific","city":"Chennai"},{"iata":"HYD","lat":17.2313175201,"lon":78.4298553467,"cca2":"IN","region":"Asia Pacific","city":"Hyderabad"},{"iata":"CNN","lat":11.915858,"lon":75.55094,"cca2":"IN","region":"Asia Pacific","city":"Kannur"},{"iata":"KNU","lat":26.4499,"lon":80.3319,"cca2":"IN","region":"Asia Pacific","city":"Kanpur"},{"iata":"COK","lat":9.9312,"lon":76.2673,"cca2":"IN","region":"Asia Pacific","city":"Kochi"},{"iata":"CCU","lat":22.6476933,"lon":88.4349249,"cca2":"IN","region":"Asia Pacific","city":"Kolkata"},{"iata":"BOM","lat":19.0886993408,"lon":72.8678970337,"cca2":"IN","region":"Asia Pacific","city":"Mumbai"},{"iata":"NAG","lat":21.1610714,"lon":79.0024702,"cca2":"IN","region":"Asia Pacific","city":"Nagpur"},{"iata":"DEL","lat":28.5664997101,"lon":77.1031036377,"cca2":"IN","region":"Asia Pacific","city":"New Delhi"},{"iata":"PAT","lat":25.591299057,"lon":85.0879974365,"cca2":"IN","region":"Asia Pacific","city":"Patna"},{"iata":"DPS","lat":-8.748169899,"lon":115.1669998169,"cca2":"ID","region":"Asia Pacific","city":"Denpasar"},{"iata":"CGK","lat":-6.1275229,"lon":106.6515118,"cca2":"ID","region":"Asia Pacific","city":"Jakarta"},{"iata":"JOG","lat":-7.7881798744,"lon":110.4319992065,"cca2":"ID","region":"Asia Pacific","city":"Yogyakarta"},{"iata":"BGW","lat":33.2625007629,"lon":44.2346000671,"cca2":"IQ","region":"Middle East","city":"Baghdad"},{"iata":"BSR","lat":30.5491008759,"lon":47.6621017456,"cca2":"IQ","region":"Middle East","city":"Basra"},{"iata":"EBL","lat":36.1901,"lon":43.993,"cca2":"IQ","region":"Middle East","city":"Erbil"},{"iata":"NJF","lat":31.989722,"lon":44.404167,"cca2":"IQ","region":"Middle East","city":"Najaf"},{"iata":"XNH","lat":30.9358005524,"lon":46.0900993347,"cca2":"IQ","region":"Middle East","city":"Nasiriyah"},{"iata":"ISU","lat":35.5668,"lon":45.4161,"cca2":"IQ","region":"Middle East","city":"Sulaymaniyah"},{"iata":"ORK","lat":51.8413009644,"lon":-8.491109848,"cca2":"IE","region":"Europe","city":"Cork"},{"iata":"DUB","lat":53.4212989807,"lon":-6.270070076,"cca2":"IE","region":"Europe","city":"Dublin"},{"iata":"HFA","lat":32.78492,"lon":34.96069,"cca2":"IL","region":"Middle East","city":"Haifa"},{"iata":"TLV","lat":32.0113983154,"lon":34.8866996765,"cca2":"IL","region":"Middle East","city":"Tel Aviv"},{"iata":"MXP","lat":45.6305999756,"lon":8.7281103134,"cca2":"IT","region":"Europe","city":"Milan"},{"iata":"PMO","lat":38.16114,"lon":13.31546,"cca2":"IT","region":"Europe","city":"Palermo"},{"iata":"FCO","lat":41.8045005798,"lon":12.2508001328,"cca2":"IT","region":"Europe","city":"Rome"},{"iata":"KIN","lat":17.9951,"lon":-76.7846,"cca2":"JM","region":"North America","city":"Kingston"},{"iata":"FUK","lat":33.5902,"lon":130.4017,"cca2":"JP","region":"Asia Pacific","city":"Fukuoka"},{"iata":"OKA","lat":26.1958,"lon":127.646,"cca2":"JP","region":"Asia Pacific","city":"Naha"},{"iata":"KIX","lat":34.4272994995,"lon":135.244003296,"cca2":"JP","region":"Asia Pacific","city":"Osaka"},{"iata":"NRT","lat":35.7647018433,"lon":140.386001587,"cca2":"JP","region":"Asia Pacific","city":"Tokyo"},{"iata":"AMM","lat":31.7226009369,"lon":35.9931983948,"cca2":"JO","region":"Middle East","city":"Amman"},{"iata":"ALA","lat":43.3521003723,"lon":77.0404968262,"cca2":"KZ","region":"Asia Pacific","city":"Almaty"},{"iata":"MBA","lat":-4.0348300934,"lon":39.5942001343,"cca2":"KE","region":"Africa","city":"Mombasa"},{"iata":"NBO","lat":-1.319239974,"lon":36.9277992249,"cca2":"KE","region":"Africa","city":"Nairobi"},{"iata":"ICN","lat":37.4691009521,"lon":126.450996399,"cca2":"KR","region":"Asia Pacific","city":"Seoul"},{"iata":"KWI","lat":29.226600647,"lon":47.9688987732,"cca2":"KW","region":"Middle East","city":"Kuwait City"},{"iata":"VTE","lat":17.9757,"lon":102.5683,"cca2":"LA","region":"Asia Pacific","city":"Vientiane"},{"iata":"RIX","lat":56.9235992432,"lon":23.9710998535,"cca2":"LV","region":"Europe","city":"Riga"},{"iata":"BEY","lat":33.8208999634,"lon":35.4883995056,"cca2":"LB","region":"Middle East","city":"Beirut"},{"iata":"VNO","lat":54.6341018677,"lon":25.2858009338,"cca2":"LT","region":"Europe","city":"Vilnius"},{"iata":"LUX","lat":49.6265983582,"lon":6.211520195,"cca2":"LU","region":"Europe","city":"Luxembourg City"},{"iata":"MFM","lat":22.1495990753,"lon":113.592002869,"cca2":"MO","region":"Asia Pacific","city":"Macau"},{"iata":"TNR","lat":-18.91368,"lon":47.53613,"cca2":"MG","region":"Africa","city":"Antananarivo"},{"iata":"JHB","lat":1.635848,"lon":103.665943,"cca2":"MY","region":"Asia Pacific","city":"Johor Bahru"},{"iata":"KUL","lat":2.745579958,"lon":101.709999084,"cca2":"MY","region":"Asia Pacific","city":"Kuala Lumpur"},{"iata":"MLE","lat":4.1748,"lon":73.50888,"cca2":"MV","region":"Asia Pacific","city":"Male"},{"iata":"MRU","lat":-20.4302005768,"lon":57.6836013794,"cca2":"MU","region":"Africa","city":"Port Louis"},{"iata":"GDL","lat":20.5217990875,"lon":-103.3109970093,"cca2":"MX","region":"North America","city":"Guadalajara"},{"iata":"MEX","lat":19.4363002777,"lon":-99.0720977783,"cca2":"MX","region":"North America","city":"Mexico City"},{"iata":"QRO","lat":20.6173000336,"lon":-100.185997009,"cca2":"MX","region":"North America","city":"Queretaro"},{"iata":"KIV","lat":46.9277000427,"lon":28.9309997559,"cca2":"MD","region":"Europe","city":"Chișinău"},{"iata":"ULN","lat":47.8431015015,"lon":106.766998291,"cca2":"MN","region":"Asia Pacific","city":"Ulaanbaatar"},{"iata":"CMN","lat":33.3675003052,"lon":-7.5899701118,"cca2":"MA","region":"Africa","city":"Casablanca"},{"iata":"MPM","lat":-25.9207992554,"lon":32.5726013184,"cca2":"MZ","region":"Africa","city":"Maputo"},{"iata":"MDL","lat":21.7051697,"lon":95.9695206,"cca2":"MM","region":"Asia Pacific","city":"Mandalay"},{"iata":"RGN","lat":16.9073009491,"lon":96.1332015991,"cca2":"MM","region":"Asia Pacific","city":"Yangon"},{"iata":"KTM","lat":27.6965999603,"lon":85.3591003418,"cca2":"NP","region":"Asia Pacific","city":"Kathmandu"},{"iata":"AMS","lat":52.3086013794,"lon":4.7638897896,"cca2":"NL","region":"Europe","city":"Amsterdam"},{"iata":"NOU","lat":-22.0146007538,"lon":166.212997436,"cca2":"NC","region":"Oceania","city":"Noumea"},{"iata":"AKL","lat":-37.0080986023,"lon":174.792007446,"cca2":"NZ","region":"Oceania","city":"Auckland"},{"iata":"CHC","lat":-43.4893989563,"lon":172.5319976807,"cca2":"NZ","region":"Oceania","city":"Christchurch"},{"iata":"LOS","lat":6.5773701668,"lon":3.321160078,"cca2":"NG","region":"Africa","city":"Lagos"},{"iata":"SKP","lat":41.9616012573,"lon":21.6214008331,"cca2":"MK","region":"Europe","city":"Skopje"},{"iata":"OSL","lat":60.193901062,"lon":11.100399971,"cca2":"NO","region":"Europe","city":"Oslo"},{"iata":"MCT","lat":23.5932998657,"lon":58.2844009399,"cca2":"OM","region":"Middle East","city":"Muscat"},{"iata":"ISB","lat":33.6166992188,"lon":73.0991973877,"cca2":"PK","region":"Asia Pacific","city":"Islamabad"},{"iata":"KHI","lat":24.9064998627,"lon":67.1607971191,"cca2":"PK","region":"Asia Pacific","city":"Karachi"},{"iata":"LHE","lat":31.5216007233,"lon":74.4036026001,"cca2":"PK","region":"Asia Pacific","city":"Lahore"},{"iata":"ZDM","lat":32.2719,"lon":35.0194,"cca2":"PS","region":"Middle East","city":"Ramallah"},{"iata":"PTY","lat":9.0713596344,"lon":-79.3834991455,"cca2":"PA","region":"South America","city":"Panama City"},{"iata":"ASU","lat":-25.2399997711,"lon":-57.5200004578,"cca2":"PY","region":"South America","city":"Asunción"},{"iata":"LIM","lat":-12.021900177,"lon":-77.1143035889,"cca2":"PE","region":"South America","city":"Lima"},{"iata":"CGY","lat":8.4156198502,"lon":124.611000061,"cca2":"PH","region":"Asia Pacific","city":"Cagayan de Oro"},{"iata":"CEB","lat":10.3074998856,"lon":123.978996277,"cca2":"PH","region":"Asia Pacific","city":"Cebu"},{"iata":"MNL","lat":14.508600235,"lon":121.019996643,"cca2":"PH","region":"Asia Pacific","city":"Manila"},{"iata":"WAW","lat":52.1656990051,"lon":20.9671001434,"cca2":"PL","region":"Europe","city":"Warsaw"},{"iata":"LIS","lat":38.7812995911,"lon":-9.1359195709,"cca2":"PT","region":"Europe","city":"Lisbon"},{"iata":"SJU","lat":18.411391,"lon":-66.102793,"cca2":"PR","region":"North America","city":"San Juan"},{"iata":"DOH","lat":25.2605946,"lon":51.6137665,"cca2":"QA","region":"Middle East","city":"Doha"},{"iata":"RUN","lat":-20.8871002197,"lon":55.5102996826,"cca2":"RE","region":"Africa","city":"Saint-Denis"},{"iata":"OTP","lat":44.5722007751,"lon":26.1021995544,"cca2":"RO","region":"Europe","city":"Bucharest"},{"iata":"KJA","lat":56.0153,"lon":92.8932,"cca2":"RU","region":"Asia Pacific","city":"Krasnoyarsk"},{"iata":"DME","lat":55.4087982178,"lon":37.9062995911,"cca2":"RU","region":"Europe","city":"Moscow"},{"iata":"LED","lat":59.8003005981,"lon":30.2625007629,"cca2":"RU","region":"Europe","city":"Saint Petersburg"},{"iata":"KLD","lat":56.8587,"lon":35.9176,"cca2":"RU","region":"Europe","city":"Tver"},{"iata":"SVX","lat":56.8431,"lon":60.6454,"cca2":"RU","region":"Asia Pacific","city":"Yekaterinburg"},{"iata":"KGL","lat":-1.9686299563,"lon":30.1394996643,"cca2":"RW","region":"Africa","city":"Kigali"},{"iata":"DMM","lat":26.471200943,"lon":49.7979011536,"cca2":"SA","region":"Middle East","city":"Dammam"},{"iata":"JED","lat":21.679599762,"lon":39.15650177,"cca2":"SA","region":"Middle East","city":"Jeddah"},{"iata":"RUH","lat":24.9575996399,"lon":46.6987991333,"cca2":"SA","region":"Middle East","city":"Riyadh"},{"iata":"DKR","lat":14.7412099,"lon":-17.4889771,"cca2":"SN","region":"Africa","city":"Dakar"},{"iata":"BEG","lat":44.8184013367,"lon":20.3090991974,"cca2":"RS","region":"Europe","city":"Belgrade"},{"iata":"SIN","lat":1.3501900434,"lon":103.994003296,"cca2":"SG","region":"Asia Pacific","city":"Singapore"},{"iata":"BTS","lat":48.1486,"lon":17.1077,"cca2":"SK","region":"Europe","city":"Bratislava"},{"iata":"CPT","lat":-33.9648017883,"lon":18.6016998291,"cca2":"ZA","region":"Africa","city":"Cape Town"},{"iata":"DUR","lat":-29.6144444444,"lon":31.1197222222,"cca2":"ZA","region":"Africa","city":"Durban"},{"iata":"JNB","lat":-26.133333,"lon":28.25,"cca2":"ZA","region":"Africa","city":"Johannesburg"},{"iata":"BCN","lat":41.2971000671,"lon":2.0784599781,"cca2":"ES","region":"Europe","city":"Barcelona"},{"iata":"MAD","lat":40.4936,"lon":-3.56676,"cca2":"ES","region":"Europe","city":"Madrid"},{"iata":"CMB","lat":7.1807599068,"lon":79.8841018677,"cca2":"LK","region":"Asia Pacific","city":"Colombo"},{"iata":"PBM","lat":5.452831,"lon":-55.187783,"cca2":"SR","region":"South America","city":"Paramaribo"},{"iata":"GOT","lat":57.6627998352,"lon":12.279800415,"cca2":"SE","region":"Europe","city":"Gothenburg"},{"iata":"ARN","lat":59.6519012451,"lon":17.9186000824,"cca2":"SE","region":"Europe","city":"Stockholm"},{"iata":"GVA","lat":46.2380981445,"lon":6.1089501381,"cca2":"CH","region":"Europe","city":"Geneva"},{"iata":"ZRH","lat":47.4646987915,"lon":8.5491695404,"cca2":"CH","region":"Europe","city":"Zurich"},{"iata":"KHH","lat":22.5771007538,"lon":120.3499984741,"cca2":"TW","region":"Asia Pacific","city":"Kaohsiung City"},{"iata":"TPE","lat":25.0776996613,"lon":121.233001709,"cca2":"TW","region":"Asia Pacific","city":"Taipei"},{"iata":"DAR","lat":-6.8781099319,"lon":39.2025985718,"cca2":"TZ","region":"Africa","city":"Dar es Salaam"},{"iata":"BKK","lat":13.6810998917,"lon":100.747001648,"cca2":"TH","region":"Asia Pacific","city":"Bangkok"},{"iata":"CNX","lat":18.7667999268,"lon":98.962600708,"cca2":"TH","region":"Asia Pacific","city":"Chiang Mai"},{"iata":"URT","lat":9.1325998306,"lon":99.135597229,"cca2":"TH","region":"Asia Pacific","city":"Surat Thani"},{"iata":"TUN","lat":36.8510017395,"lon":10.2271995544,"cca2":"TN","region":"Africa","city":"Tunis"},{"iata":"IST","lat":40.9768981934,"lon":28.8145999908,"cca2":"TR","region":"Europe","city":"Istanbul"},{"iata":"ADB","lat":38.32377,"lon":27.14317,"cca2":"TR","region":"Europe","city":"Izmir"},{"iata":"KBP","lat":50.3450012207,"lon":30.8946990967,"cca2":"UA","region":"Europe","city":"Kyiv"},{"iata":"DXB","lat":25.2527999878,"lon":55.3643989563,"cca2":"AE","region":"Middle East","city":"Dubai"},{"iata":"EDI","lat":55.9500007629,"lon":-3.3724999428,"cca2":"GB","region":"Europe","city":"Edinburgh"},{"iata":"LHR","lat":51.4706001282,"lon":-0.4619410038,"cca2":"GB","region":"Europe","city":"London"},{"iata":"MAN","lat":53.3536987305,"lon":-2.2749500275,"cca2":"GB","region":"Europe","city":"Manchester"},{"iata":"MGM","lat":32.30059814,"lon":-86.39399719,"cca2":"US","region":"North America","city":"Montgomery"},{"iata":"ANC","lat":61.158555,"lon":-149.890208,"cca2":"US","region":"North America","city":"Anchorage"},{"iata":"PHX","lat":33.434299469,"lon":-112.012001038,"cca2":"US","region":"North America","city":"Phoenix"},{"iata":"LAX","lat":33.94250107,"lon":-118.4079971,"cca2":"US","region":"North America","city":"Los Angeles"},{"iata":"SMF","lat":38.695400238,"lon":-121.591003418,"cca2":"US","region":"North America","city":"Sacramento"},{"iata":"SAN","lat":32.7336006165,"lon":-117.190002441,"cca2":"US","region":"North America","city":"San Diego"},{"iata":"SFO","lat":37.6189994812,"lon":-122.375,"cca2":"US","region":"North America","city":"San Francisco"},{"iata":"SJC","lat":37.3625984192,"lon":-121.929000855,"cca2":"US","region":"North America","city":"San Jose"},{"iata":"DEN","lat":39.8616981506,"lon":-104.672996521,"cca2":"US","region":"North America","city":"Denver"},{"iata":"JAX","lat":30.4941005707,"lon":-81.6878967285,"cca2":"US","region":"North America","city":"Jacksonville"},{"iata":"MIA","lat":25.7931995392,"lon":-80.2906036377,"cca2":"US","region":"North America","city":"Miami"},{"iata":"TLH","lat":30.3964996338,"lon":-84.3503036499,"cca2":"US","region":"North America","city":"Tallahassee"},{"iata":"TPA","lat":27.9755001068,"lon":-82.533203125,"cca2":"US","region":"North America","city":"Tampa"},{"iata":"ATL","lat":33.6366996765,"lon":-84.4281005859,"cca2":"US","region":"North America","city":"Atlanta"},{"iata":"HNL","lat":21.3187007904,"lon":-157.9219970703,"cca2":"US","region":"North America","city":"Honolulu"},{"iata":"ORD","lat":41.97859955,"lon":-87.90480042,"cca2":"US","region":"North America","city":"Chicago"},{"iata":"IND","lat":39.717300415,"lon":-86.2944030762,"cca2":"US","region":"North America","city":"Indianapolis"},{"iata":"BGR","lat":44.8081,"lon":-68.795,"cca2":"US","region":"North America","city":"Bangor"},{"iata":"BOS","lat":42.36429977,"lon":-71.00520325,"cca2":"US","region":"North America","city":"Boston"},{"iata":"DTW","lat":42.2123985291,"lon":-83.3534011841,"cca2":"US","region":"North America","city":"Detroit"},{"iata":"MSP","lat":44.8819999695,"lon":-93.2218017578,"cca2":"US","region":"North America","city":"Minneapolis"},{"iata":"MCI","lat":39.2975997925,"lon":-94.7138977051,"cca2":"US","region":"North America","city":"Kansas City"},{"iata":"STL","lat":38.7486991882,"lon":-90.3700027466,"cca2":"US","region":"North America","city":"St. Louis"},{"iata":"OMA","lat":41.3031997681,"lon":-95.8940963745,"cca2":"US","region":"North America","city":"Omaha"},{"iata":"LAS","lat":36.08010101,"lon":-115.1520004,"cca2":"US","region":"North America","city":"Las Vegas"},{"iata":"EWR","lat":40.6925010681,"lon":-74.1687011719,"cca2":"US","region":"North America","city":"Newark"},{"iata":"ABQ","lat":35.0844,"lon":-106.6504,"cca2":"US","region":"North America","city":"Albuquerque"},{"iata":"BUF","lat":42.94049835,"lon":-78.73220062,"cca2":"US","region":"North America","city":"Buffalo"},{"iata":"CLT","lat":35.2140007019,"lon":-80.9430999756,"cca2":"US","region":"North America","city":"Charlotte"},{"iata":"RDU","lat":35.93543,"lon":-78.88075,"cca2":"US","region":"North America","city":"Durham"},{"iata":"CLE","lat":41.50069,"lon":-81.68412,"cca2":"US","region":"North America","city":"Cleveland"},{"iata":"CMH","lat":39.9980010986,"lon":-82.8918991089,"cca2":"US","region":"North America","city":"Columbus"},{"iata":"OKC","lat":35.46655,"lon":-97.65373,"cca2":"US","region":"North America","city":"Oklahoma City"},{"iata":"PDX","lat":45.58869934,"lon":-122.5979996,"cca2":"US","region":"North America","city":"Portland"},{"iata":"PHL","lat":39.8718986511,"lon":-75.2410964966,"cca2":"US","region":"North America","city":"Philadelphia"},{"iata":"PIT","lat":40.49150085,"lon":-80.23290253,"cca2":"US","region":"North America","city":"Pittsburgh"},{"iata":"FSD","lat":43.540819819502,"lon":-96.65511577730963,"cca2":"US","region":"North America","city":"Sioux Falls"},{"iata":"MEM","lat":35.0424003601,"lon":-89.9766998291,"cca2":"US","region":"North America","city":"Memphis"},{"iata":"BNA","lat":36.1245002747,"lon":-86.6781997681,"cca2":"US","region":"North America","city":"Nashville"},{"iata":"AUS","lat":30.1975,"lon":-97.6664,"cca2":"US","region":"North America","city":"Austin"},{"iata":"DFW","lat":32.8968009949,"lon":-97.0380020142,"cca2":"US","region":"North America","city":"Dallas"},{"iata":"IAH","lat":29.9843997955,"lon":-95.3414001465,"cca2":"US","region":"North America","city":"Houston"},{"iata":"MFE","lat":26.17580032,"lon":-98.23860168,"cca2":"US","region":"North America","city":"McAllen"},{"iata":"SAT","lat":29.429461,"lon":-98.487061,"cca2":"US","region":"North America","city":"San Antonio"},{"iata":"SLC","lat":40.7883987427,"lon":-111.977996826,"cca2":"US","region":"North America","city":"Salt Lake City"},{"iata":"IAD","lat":38.94449997,"lon":-77.45580292,"cca2":"US","region":"North America","city":"Ashburn"},{"iata":"ORF","lat":36.8945999146,"lon":-76.2012023926,"cca2":"US","region":"North America","city":"Norfolk"},{"iata":"RIC","lat":37.5051994324,"lon":-77.3197021484,"cca2":"US","region":"North America","city":"Richmond"},{"iata":"SEA","lat":47.4490013123,"lon":-122.308998108,"cca2":"US","region":"North America","city":"Seattle"},{"iata":"TAS","lat":41.257900238,"lon":69.2811965942,"cca2":"UZ","region":"Asia Pacific","city":"Tashkent"},{"iata":"DAD","lat":16.02636,"lon":108.20869,"cca2":"VN","region":"Asia Pacific","city":"Da Nang"},{"iata":"HAN","lat":21.221200943,"lon":105.806999206,"cca2":"VN","region":"Asia Pacific","city":"Hanoi"},{"iata":"SGN","lat":10.8187999725,"lon":106.652000427,"cca2":"VN","region":"Asia Pacific","city":"Ho Chi Minh City"},{"iata":"HRE","lat":-17.9318008423,"lon":31.0928001404,"cca2":"ZW","region":"Africa","city":"Harare"}] -------------------------------------------------------------------------------- /data/相关程序的备份/2_ipspeedtest/使用ipspeedtest测试延迟和速度.bat: -------------------------------------------------------------------------------- 1 | ipspeedtest.exe -file=ip.txt -outfile=ip.csv -port=443 -max=200 -------------------------------------------------------------------------------- /data/相关程序的备份/命令示例.txt: -------------------------------------------------------------------------------- 1 | CloudflareST.exe程序的来源: 2 | 3 | https://github.com/XIU2/CloudflareSpeedTest 4 | 5 | 测延迟(禁止测速) 6 | 7 | CloudflareST.exe -f ipv4.txt -tp 443 -o result.csv -dd 8 | 9 | CloudflareST.exe -tl 300 -tll 10 -f ipv4.txt -tp 443 -o result.csv -dd 10 | 11 | 测速100个(需要很长时间) 12 | 13 | CloudflareST.exe -tl 300 -tll 10 -f ipv4.txt -tp 443 -o result.csv -url https://speed.cloudflare.com/__down?bytes=500000000 -dn 100 14 | 15 | ———————————————————————————————— 16 | 17 | ipspeedtest.exe 程序的来源: 18 | 19 | https://github.com/badafans/Cloudflare-IP-SpeedTest -------------------------------------------------------------------------------- /images/图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juerson/actix-cfwks-subconverter-yaml/6f558249fc481fe026b42b1ec1e41e050da64df8/images/图1.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use actix_web::{ get, web, App, HttpRequest, HttpResponse, HttpServer, Responder }; 4 | use clap::{ error::ErrorKind, CommandFactory, Parser }; 5 | use lazy_static::lazy_static; 6 | use local_ip_address::local_ip; 7 | use serde_urlencoded::from_str; 8 | use serde_yaml::Value as YamlValue; 9 | use utils::{ build, config, qrcode }; 10 | 11 | const SPECIFICATION: &str = include_str!("../使用说明.txt"); 12 | 13 | /// 基于HTTP传输协议的vless+ws[+tls]、trojan+ws[+tls]、ss-v2ray[+tls]转换v2ray、sing-box、clash订阅工具! 14 | #[derive(Parser, Debug, Clone)] 15 | #[command(version, about, long_about = None)] 16 | struct Args { 17 | /// HTTP服务器的端口 18 | #[arg(short, long, default_value = "10111")] 19 | port: u16, 20 | 21 | /// 默认转换为v2ray,可选singbox、clash 22 | #[arg(long, default_value = "v2ray")] 23 | target: String, 24 | } 25 | 26 | // 共享Args结构体中的数据状态(让Args在其它地方使用) 27 | struct AppState { 28 | args: Args, 29 | } 30 | 31 | #[derive(Default, Clone)] 32 | pub struct Params { 33 | pub target: String, 34 | pub node_count: usize, 35 | pub default_port: u16, 36 | pub userid: u8, 37 | pub column_name: String, 38 | pub template: bool, 39 | pub proxy_type: String, 40 | pub tls_mode: String, 41 | pub data_source: String, 42 | pub page: usize, 43 | } 44 | 45 | lazy_static! { 46 | static ref CONFIG_FILE: &'static str = "config.yaml"; 47 | static ref CLASH_TEMPLATE: &'static str = "template/clash.yaml"; 48 | static ref SINGBOX_TEMPLATE: &'static str = "template/sing-box.json"; 49 | } 50 | 51 | async fn default_route() -> impl Responder { 52 | HttpResponse::NotFound().body("Not found.") 53 | } 54 | 55 | #[get("/")] 56 | async fn index(req: HttpRequest) -> impl Responder { 57 | let host_address = req.connection_info().host().to_owned(); 58 | 59 | let html_body = SPECIFICATION.replace("127.0.0.1:10111", &host_address); 60 | 61 | // 获取当前局域网IP地址 62 | let ip_address = local_ip().unwrap().to_string(); 63 | 64 | // 获取当前URL 65 | let url = format!( 66 | "{}://{}{}", 67 | req.connection_info().scheme(), 68 | req.connection_info().host().replace("127.0.0.1", &ip_address), 69 | req.uri() 70 | ); 71 | 72 | // 生成二维码并将html_body嵌入网页中 73 | let html_content = qrcode::generate_html_with_qrcode(&html_body, &url); 74 | 75 | HttpResponse::Ok().content_type("text/html; charset=utf-8").body(html_content) 76 | } 77 | 78 | #[get("/sub")] 79 | async fn subconverter(req: HttpRequest, data: web::Data) -> impl Responder { 80 | let query_str = req.query_string(); 81 | let params: Vec<(String, String)> = from_str(&query_str).expect("Failed to parse query string"); 82 | 83 | let mut uri_params = Params { 84 | target: data.args.target.to_string(), // 由cli参数中传递进来,默认转换为v2ray,可以在订阅链接中修改 85 | node_count: 300, // 节点数量,这里默认300,实际不一定是这个数字 86 | default_port: 0, // 默认端口,没有在数据文件读取到端口才启用它,0为随机端口 87 | userid: 0, // 选择yaml中哪个节点配置(index) 88 | column_name: "colo".to_string(), // 使用哪个列名的字段值为节点的前缀?可选:[colo,loc,region,city] 89 | template: true, // 是否使用模板文件,默认使用 90 | proxy_type: "all".to_string(), // 不区分代理的类型(vles、trojan) 91 | tls_mode: "all".to_string(), // 选择哪些端口?true/1是选择TLS端口,false/0选择非TLS的端口,其它就不区分 92 | data_source: "./data".to_string(), // 默认数据文件路径 93 | page: 1, 94 | }; 95 | 96 | // 获取url的参数 97 | for (key, value) in params { 98 | if key.to_lowercase() == "target" { 99 | uri_params.target = value.to_string().to_string(); 100 | } else if vec!["n", "nodesize", "nodecount"].contains(&key.to_lowercase().as_str()) { 101 | uri_params.node_count = value.parse::().unwrap_or(uri_params.node_count); 102 | } else if vec!["dport", "defaultport"].contains(&key.to_lowercase().as_str()) { 103 | if let Ok(port) = value.parse::() { 104 | if (80..65535).contains(&port) { 105 | uri_params.default_port = port; 106 | } 107 | } 108 | } else if vec!["id", "userid"].contains(&key.to_lowercase().as_str()) { 109 | if let Ok(1..=255) = value.parse::() { 110 | uri_params.userid = value.parse::().unwrap(); 111 | } 112 | } else if key.to_lowercase() == "page" { 113 | uri_params.page = value.parse().unwrap_or(uri_params.page).max(1); 114 | } else if key.to_lowercase() == "template" { 115 | uri_params.template = value.parse::().unwrap_or(true); 116 | } else if vec!["type", "proxy", "proxytype"].contains(&key.to_lowercase().as_str()) { 117 | uri_params.proxy_type = value.to_string(); 118 | } else if vec!["column", "columnname"].contains(&key.to_lowercase().as_str()) { 119 | uri_params.column_name = value.to_string(); // 以哪个列的字段名作为前缀?[colo,loc,region,city] 120 | } else if vec!["source", "datasource"].contains(&key.to_lowercase().as_str()) { 121 | uri_params.data_source = value.to_string(); // 数据文件路径,支持相对路径和绝对路径 122 | } else if vec!["tls", "mode", "tls_mode"].contains(&key.to_lowercase().as_str()) { 123 | match value.to_string().to_lowercase().as_str() { 124 | "1" | "true" => { 125 | uri_params.tls_mode = "true".to_string(); 126 | } 127 | "0" | "false" => { 128 | uri_params.tls_mode = "false".to_string(); 129 | } 130 | _ => {} 131 | } 132 | } 133 | } 134 | 135 | let proxies_value: YamlValue = config::parse_file_to_yamlvlaue(&CONFIG_FILE); 136 | 137 | // 分拣数据以及创建订阅内容 138 | let html_body = build::sorting_data_and_build_subscribe( 139 | proxies_value, 140 | uri_params.clone(), 141 | &CLASH_TEMPLATE, 142 | &SINGBOX_TEMPLATE 143 | ); 144 | 145 | HttpResponse::Ok().content_type("text/plain; charset=utf-8").body(html_body) 146 | } 147 | 148 | #[actix_web::main] 149 | async fn main() -> std::io::Result<()> { 150 | // 获取命令行参数 151 | let result = Args::try_parse(); 152 | match result { 153 | Ok(args) => { 154 | // 将args的cli参数值分享/传递给subconverter函数中使用 155 | let shared_state = web::Data::new(AppState { args: args.clone() }); 156 | // 获取本机的私有IP地址 157 | let local_ip = match local_ip() { 158 | Ok(ip) => ip, 159 | Err(e) => { 160 | eprintln!("Failed to get local IP address: {}", e); 161 | return Ok(()); 162 | } 163 | }; 164 | // 绑定的端口 165 | let port = args.port; 166 | println!( 167 | "Server is running on http://{}:{} or http://127.0.0.1:{}", 168 | local_ip.to_string(), 169 | port, 170 | port 171 | ); 172 | return HttpServer::new(move || { 173 | App::new() 174 | .app_data(shared_state.clone()) 175 | .service(index) 176 | .service(subconverter) 177 | .default_service(actix_web::web::route().to(default_route)) 178 | }) 179 | .bind(format!("0.0.0.0:{}", port))? 180 | .run().await; 181 | } 182 | Err(e) => { 183 | if 184 | e.kind() == ErrorKind::MissingRequiredArgument || 185 | e.kind() == ErrorKind::InvalidValue 186 | { 187 | // 如果是因为缺少必需参数或无效值导致的错误,则显示帮助信息 188 | Args::command().print_help().unwrap(); 189 | } else { 190 | // 其他类型的错误则正常打印错误信息 191 | e.print().unwrap(); 192 | } 193 | } 194 | } 195 | return Ok(()); 196 | } 197 | -------------------------------------------------------------------------------- /src/utils/build.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | convert, 3 | file_data::{self, MyData}, 4 | net_data, 5 | }; 6 | use crate::Params; 7 | 8 | use lazy_static::lazy_static; 9 | use regex::Regex; 10 | use serde_json::{json, Value as JsonValue}; 11 | use serde_yaml::Value as YamlValue; 12 | 13 | lazy_static! { 14 | // 匹配包含 "name:" 的 "- {}" 字符串,应用到clash相关代码中 15 | static ref PROXYIES_NAME_REGEX: Regex = Regex::new( 16 | r" - \{([^}]*(name:[^}]*)[^}]*)\}" 17 | ).unwrap(); 18 | static ref HTTP_PORTS: [u16; 7] = [80, 8080, 8880, 2052, 2082, 2086, 2095]; 19 | static ref HTTPS_PORTS: [u16; 6] = [443, 2053, 2083, 2087, 2096, 8443]; 20 | } 21 | 22 | pub fn get_vec_data(uri_params: Params) -> Vec> { 23 | // 针对win11中"复制文件地址"出现双引号的情况 24 | let trimmed_quotes_path = uri_params.data_source.trim_matches('"'); 25 | 26 | // 从文件中读取数据,最大读取数,数据没有过滤 27 | let max_line: usize = 10000; 28 | 29 | // 获取数据(网络数据/本地数据) 30 | let my_datas: Vec = if trimmed_quotes_path.to_lowercase().starts_with("https://") { 31 | // 传入的一个https://链接,就从网络获取数据 32 | net_data::process_network_data( 33 | &uri_params.column_name, 34 | uri_params.default_port, 35 | max_line, 36 | trimmed_quotes_path, 37 | ) 38 | } else { 39 | // 传入的是本地文件路径,就从本地获取数据 40 | file_data::process_files_data( 41 | &uri_params.column_name, // 获取指定字段的数据作为节点别名的前缀 42 | uri_params.default_port, // 没有找到端口的情况,就使用它 43 | max_line, // 获取指定数量的数据就返回 44 | trimmed_quotes_path, // 指定数据源所在文件夹路径或文件路径 45 | ) 46 | }; 47 | 48 | if !my_datas.is_empty() { 49 | // ———————————————————————————————— 过滤不要的数据 —————————————————————————————— 50 | 51 | // 根据TLS模式是否开启,反向剔除不要端口的数据 52 | let filter_ports = match uri_params.tls_mode.as_str() { 53 | "true" | "1" | "all" => HTTP_PORTS.to_vec(), // 过滤掉非TLS模式的端口 54 | "false" | "0" => HTTPS_PORTS.to_vec(), // 过滤掉TLS模式的端口 55 | _ => HTTP_PORTS.to_vec(), 56 | }; 57 | let filtered_data: Vec = my_datas 58 | .iter() 59 | .filter(|item| { 60 | // 端口不在filter_ports中,则保留 61 | if let Some(port) = item.port { 62 | !filter_ports.contains(&port) 63 | } else { 64 | true // 如果port为None,保留该元素 65 | } 66 | }) 67 | .cloned() 68 | .collect(); 69 | 70 | // —————————————————————————————————— 数据分页 —————————————————————————————————— 71 | 72 | // 定义每页的最大长度(元素个数),主要限制singbox、clash配置文件最多节点数 73 | let page_size = match uri_params.target.as_str() { 74 | "singbox" | "clash" => match (1..151).contains(&uri_params.node_count) { 75 | true => uri_params.node_count, 76 | false => 50, 77 | }, 78 | _ => uri_params.node_count, 79 | }; 80 | 81 | // 将 Vec 转换为 Vec> 82 | let paginated_data: Vec> = filtered_data 83 | .chunks(page_size) 84 | .map(|chunk| chunk.to_vec()) 85 | .collect(); 86 | return paginated_data; 87 | } 88 | 89 | return Vec::new(); 90 | } 91 | 92 | /// 分拣数据以及创建订阅内容 93 | pub fn sorting_data_and_build_subscribe( 94 | all_proxies_yaml: YamlValue, 95 | uri_params: Params, 96 | clash_template: &str, 97 | singbox_template: &str, 98 | ) -> String { 99 | let paginated_data = get_vec_data(uri_params.clone()); 100 | 101 | match paginated_data.get(uri_params.page - 1) { 102 | Some(page_data) => { 103 | // 没有数据,就返回空字符串 104 | if page_data.is_empty() { 105 | return String::new(); 106 | } 107 | 108 | // 下面的代码块,通过不同的转换,获取节点名称和节点配置或v2ray链接 109 | let mut proxy_name_vec = Vec::new(); 110 | let mut nodes_vec = Vec::new(); 111 | for item in page_data { 112 | let csv_alias = item.alias.clone().unwrap_or("".to_string()); 113 | let csv_addr = item.addr.clone(); 114 | let csv_port = item.port.unwrap_or(uri_params.default_port); 115 | let (proxy_name, node) = convert::subconvert( 116 | csv_alias, 117 | csv_addr, 118 | csv_port, 119 | all_proxies_yaml.clone(), 120 | uri_params.target.clone(), 121 | uri_params.proxy_type.clone(), 122 | uri_params.tls_mode.clone(), 123 | uri_params.userid.clone(), 124 | &HTTP_PORTS, 125 | &HTTPS_PORTS, 126 | ); 127 | if !node.is_empty() && !nodes_vec.contains(&node) { 128 | nodes_vec.push(node); 129 | } 130 | if !proxy_name.is_empty() 131 | && vec!["clash", "singbox"].contains(&uri_params.target.as_str()) 132 | && !proxy_name_vec.contains(&proxy_name) 133 | { 134 | proxy_name_vec.push(proxy_name); 135 | } 136 | } 137 | 138 | // 防止没有nodes_vec数据 139 | if nodes_vec.is_empty() { 140 | return String::new(); 141 | } 142 | 143 | // 前面获取到数据后,开始构建完整的配置文件订阅(或分享链接订阅) 144 | let full_subscribe = build_full_subscribe( 145 | uri_params.target, 146 | uri_params.template, 147 | proxy_name_vec, 148 | nodes_vec, 149 | clash_template, 150 | singbox_template, 151 | ); 152 | 153 | full_subscribe 154 | } 155 | None => { 156 | // 如果没有数据,就返回空字符串 157 | return String::new(); 158 | } 159 | } 160 | } 161 | 162 | /// 将生成的nodes_vec节点信息,构建完整的订阅(或分享链接订阅) 163 | fn build_full_subscribe( 164 | target: String, 165 | enable_template: bool, 166 | proxy_name_vec: Vec, 167 | nodes_vec: Vec, 168 | clash_template: &str, 169 | singbox_template: &str, 170 | ) -> String { 171 | let mut html_body = String::new(); 172 | match target.as_str() { 173 | "clash" => { 174 | match enable_template { 175 | true => { 176 | // 读取模板文件 177 | let content: String = std::fs::read_to_string(clash_template).unwrap(); 178 | // 替换模板文件中的内容 179 | if !proxy_name_vec.is_empty() && !content.is_empty() { 180 | html_body = PROXYIES_NAME_REGEX 181 | .replace_all(&content, &nodes_vec.join("\n")) 182 | .replace( 183 | " - 127.0.0.1:1080", 184 | &proxy_name_vec 185 | .clone() 186 | .iter_mut() 187 | .map(|name| format!(" - {}", name)) 188 | .collect::>() 189 | .join("\n"), 190 | ); 191 | } 192 | } 193 | false => { 194 | html_body = format!("proxies:\n{}", nodes_vec.join("\n")); 195 | } 196 | } 197 | } 198 | "singbox" => { 199 | match enable_template { 200 | true => { 201 | let content = std::fs::read_to_string(singbox_template).unwrap(); 202 | // 读取模板文件以及解析为JSON 203 | let singbox_json: JsonValue = 204 | serde_json::from_str(&content).unwrap_or_default(); 205 | // 运用插入/retain()等操作修改模板文件的内容 206 | if !proxy_name_vec.is_empty() && singbox_json.is_object() { 207 | let mut singbox_config = singbox_json.clone(); 208 | if let Some(outbounds) = singbox_config["outbounds"].as_array_mut() { 209 | // 将节点插入到outbounds中 210 | for json_str in &nodes_vec { 211 | let parsed_json = 212 | serde_json::from_str(json_str).expect("Failed to parse JSON"); 213 | outbounds.insert(2, parsed_json); // 插入到第3个位置 214 | } 215 | outbounds.iter_mut().for_each(|item| { 216 | if let Some(obj) = item.as_object_mut() { 217 | obj.get_mut("outbounds") 218 | .and_then(JsonValue::as_array_mut) 219 | .map(|inside_outbounds| { 220 | // 使用 retain 方法来过滤掉 "{all}" 221 | inside_outbounds 222 | .retain(|x| x.as_str() != Some("{all}")); 223 | // 添加 proxy_name 到内层的 outbounds 中 224 | inside_outbounds.extend( 225 | proxy_name_vec 226 | .iter() 227 | .map(|s| JsonValue::String(s.clone())), 228 | ); 229 | }); 230 | } 231 | }); 232 | } 233 | html_body = serde_json::to_string_pretty(&singbox_config).unwrap(); 234 | } 235 | } 236 | false => { 237 | let mut outbounds = json!({"outbounds": []}); 238 | if let Some(array) = outbounds["outbounds"].as_array_mut() { 239 | nodes_vec.iter().for_each(|name| { 240 | array.push(serde_json::from_str(name).unwrap()); 241 | }); 242 | } 243 | html_body = serde_json::to_string_pretty(&outbounds).unwrap(); 244 | } 245 | } 246 | } 247 | _ => { 248 | html_body = nodes_vec.join("\n"); // 视为v2ray订阅的分享链接 249 | } 250 | } 251 | 252 | html_body 253 | } 254 | -------------------------------------------------------------------------------- /src/utils/clash.rs: -------------------------------------------------------------------------------- 1 | use serde_yaml::Value as YamlValue; 2 | 3 | // 原来就是clash配置的,直接修改对应的值即可 4 | pub fn build_clash_yaml( 5 | yaml_value: &mut YamlValue, 6 | remarks: String, 7 | server_address: String, 8 | server_port: u16 9 | ) -> &mut YamlValue { 10 | if let YamlValue::Mapping(map) = yaml_value { 11 | if let Some(name_value) = map.get_mut("name") { 12 | *name_value = YamlValue::String(remarks); 13 | } 14 | if let Some(server_value) = map.get_mut("server") { 15 | *server_value = YamlValue::String(server_address); 16 | } 17 | if let Some(port_value) = map.get_mut("port") { 18 | *port_value = YamlValue::Number(server_port.into()); 19 | } 20 | } 21 | yaml_value 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/config.rs: -------------------------------------------------------------------------------- 1 | use serde_yaml::{ self, Value as YamlValue }; 2 | use std::{ fs::File, io::{ BufReader, Read } }; 3 | 4 | // 将YAML文件解析为 serde_yaml::Value 5 | pub fn parse_file_to_yamlvlaue(file_path: &str) -> YamlValue { 6 | let file = match File::open(file_path) { 7 | Ok(f) => f, 8 | Err(_) => { 9 | return YamlValue::Null; 10 | } 11 | }; 12 | let mut reader = BufReader::new(file); 13 | let mut yaml_content = String::new(); 14 | 15 | if reader.read_to_string(&mut yaml_content).is_err() { 16 | return YamlValue::Null; 17 | } 18 | 19 | let yaml_value = serde_yaml::from_str(&yaml_content).unwrap_or(YamlValue::Null); 20 | 21 | yaml_value 22 | .get("proxies") 23 | .filter(|v| v.is_sequence()) // 只获取"proxies"键的值是序列的 24 | .cloned() 25 | .unwrap_or(yaml_value) // 如果没有找到"proxies"键,则返回整个yaml_value值 26 | } 27 | 28 | // 递归查找 YAML 值 29 | pub fn get_yaml_value<'a>(yaml: &'a YamlValue, keys: &[&str]) -> Option<&'a YamlValue> { 30 | let mut current = yaml; 31 | for key in keys { 32 | // 查找忽略大小写的键 33 | current = current 34 | .as_mapping()? 35 | .iter() 36 | .find(|(k, _)| { 37 | k.as_str().map_or(false, |k_str| k_str.to_lowercase() == key.to_lowercase()) 38 | }) 39 | .map(|(_, v)| v)?; 40 | } 41 | Some(current) 42 | } 43 | 44 | // 多个keys备选查找函数 45 | pub fn get_yaml_value_with_fallback<'a>(yaml: &'a YamlValue, keys: &[&str]) -> Option<&'a str> { 46 | keys.iter() 47 | .filter_map(|&key| get_yaml_value(yaml, &[key]).and_then(|v| v.as_str())) 48 | .next() 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/convert.rs: -------------------------------------------------------------------------------- 1 | use super::{clash, config::get_yaml_value, singbox, v2ray}; 2 | use rand::{seq::SliceRandom, Rng}; 3 | use serde_yaml::Value as YamlValue; 4 | 5 | pub fn subconvert( 6 | csv_alias: String, 7 | csv_addr: String, 8 | mut port: u16, 9 | yamlvalue: YamlValue, 10 | uri_target: String, 11 | uri_proxy_type: String, 12 | uri_tls_mode: String, 13 | uri_userid: u8, 14 | http_ports: &[u16; 7], 15 | https_ports: &[u16; 6], 16 | ) -> (String, String) { 17 | // 判断端口类型的闭包 18 | let is_https_ports = move |port: u16| -> bool { https_ports.contains(&port) }; 19 | let is_http_ports = move |port: u16| -> bool { http_ports.contains(&port) }; 20 | 21 | let csv_remarks = match csv_alias.is_empty() { 22 | true => String::new(), 23 | false => format!("{} | ", csv_alias), 24 | }; 25 | if let Some(sequence) = yamlvalue.clone().as_sequence_mut() { 26 | let length = sequence.len(); 27 | let mut index; 28 | 29 | // 循环200次,直到选中合适的节点配置为止,或循环200次才跳出循环 30 | for _ in 0..200 { 31 | // 使用config.yaml中具体哪个节点的配置 32 | index = match (1..=(length + 1) as u8).contains(&uri_userid) { 33 | true => (uri_userid as usize) - 1, // 选择指定的(数组的下标) 34 | false => rand::thread_rng().gen_range(0..length), // 随机选择(数组的下标) 35 | }; 36 | 37 | let random_https_port = https_ports.choose(&mut rand::thread_rng()).unwrap_or(&443); 38 | let random_http_port = http_ports.choose(&mut rand::thread_rng()).unwrap_or(&8080); 39 | 40 | if let Some(yaml_value) = sequence.get_mut(index) { 41 | let mut yaml_value_clone = yaml_value.clone(); 42 | 43 | let node_type = yaml_value_clone 44 | .get("type") 45 | .and_then(|v| v.as_str()) 46 | .map(|s| s.to_string()) 47 | .unwrap_or_else(String::new); 48 | 49 | let host = match node_type.as_str() { 50 | "ss" => get_yaml_value(&yaml_value_clone, &["plugin-opts", "host"]) 51 | .and_then(|v| v.as_str()) 52 | .unwrap_or_default(), 53 | "vless" | "trojan" => { 54 | get_yaml_value(&yaml_value_clone, &["ws-opts", "headers", "Host"]) 55 | .and_then(|v| v.as_str()) 56 | .unwrap_or_default() 57 | } 58 | _ => { 59 | continue; 60 | } 61 | }; 62 | 63 | // 处理 port 、 workers.dev、tls 之间与端口的问题,端口不对就随机生成一个 64 | let is_workers_dev = host.ends_with("workers.dev"); 65 | if node_type == "ss" { 66 | let tls_val = get_yaml_value(&yaml_value_clone, &["plugin-opts", "tls"]) 67 | .and_then(|v| v.as_bool()) 68 | .unwrap_or(true); 69 | if tls_val && (port == 0 || is_http_ports(port)) { 70 | port = *random_https_port; 71 | } else if !tls_val && (port == 0 || is_https_ports(port)) { 72 | port = *random_http_port; 73 | } 74 | } else if ["vless", "trojan"].contains(&node_type.as_str()) { 75 | if is_workers_dev && (port == 0 || is_https_ports(port)) { 76 | port = *random_http_port; 77 | } else if !is_workers_dev && (port == 0 || is_http_ports(port)) { 78 | port = *random_https_port; 79 | } 80 | } 81 | 82 | // 根据uri的tls筛选节点配置,是否使用这个节点生成订阅 83 | if (is_http_ports(port) && uri_tls_mode == "true") 84 | || (is_https_ports(port) && uri_tls_mode == "false") 85 | { 86 | continue; 87 | } 88 | 89 | if uri_proxy_type == node_type || uri_proxy_type == "all" { 90 | // 节点序号/账号的序号(从1开始) 91 | let padded_index = 92 | format!("{:0width$}", index + 1, width = length.to_string().len()); 93 | // 构建完整的节点名称 94 | let remarks: String = format!( 95 | "【{}】{}{}:{}", 96 | padded_index, 97 | csv_remarks, 98 | csv_addr.clone(), 99 | port 100 | ); 101 | match uri_target.as_str() { 102 | "v2ray" => { 103 | let (remarks_name, link) = v2ray::build_v2ray_links( 104 | &node_type, 105 | &mut yaml_value_clone, 106 | remarks, 107 | csv_addr, 108 | port, 109 | ); 110 | if !remarks_name.is_empty() { 111 | return (remarks_name, link); 112 | } 113 | } 114 | "clash" => { 115 | let clash_node = clash::build_clash_yaml( 116 | &mut yaml_value_clone, 117 | remarks.clone(), 118 | csv_addr, 119 | port, 120 | ); 121 | let json_node: String = serde_json::to_string(&clash_node).unwrap(); 122 | let json_string = format!(" - {json_node}"); 123 | return (remarks, json_string); 124 | } 125 | "singbox" => { 126 | let (remarks_name, json_string) = singbox::build_singbox_config_json( 127 | &node_type, 128 | &mut yaml_value_clone, 129 | remarks, 130 | csv_addr, 131 | port, 132 | ); 133 | if !remarks_name.is_empty() { 134 | return (remarks_name, json_string); 135 | } 136 | } 137 | 138 | _ => {} 139 | } 140 | 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | // 返回的前面是节点名称,后面是节点配置 148 | return (String::new(), String::new()); 149 | } 150 | -------------------------------------------------------------------------------- /src/utils/file_data.rs: -------------------------------------------------------------------------------- 1 | use csv::ReaderBuilder; 2 | use std::{ 3 | collections::HashMap, 4 | error::Error, 5 | fs::{ self, File }, 6 | io::{ BufRead, BufReader }, 7 | path::Path, 8 | vec, 9 | }; 10 | use regex::Regex; 11 | use lazy_static::lazy_static; 12 | 13 | #[derive(Default, Clone)] 14 | pub struct MyData { 15 | pub addr: String, 16 | pub port: Option, 17 | pub alias: Option, 18 | } 19 | 20 | #[derive(Default)] 21 | pub struct FileData { 22 | pub addr: String, // IP地址或者域名地址 23 | pub port: Option, 24 | pub colo: Option, // 数据中心(3位字母) 25 | pub loc: Option, // 国家代码/地区代码(2位字母) 26 | pub region: Option, // 地区 27 | pub city: Option, 28 | } 29 | 30 | lazy_static! { 31 | // 匹配一个或多个空白字符 32 | pub static ref SPACE_REGEX: Regex = Regex::new(r"\s+").unwrap(); 33 | // 匹配"IPv4 PORT"(可以1个以上的空格) 34 | pub static ref IPV4_PORT_SPACE_REGEX: Regex = Regex::new(r"^\s*([0-9.]+)\s+(\d+)\s*$").unwrap(); 35 | // 匹配"IPv6 PORT"(可以1个以上的空格) 36 | pub static ref IPV6_PORT_SPACE_REGEX: Regex = Regex::new( 37 | r"^\s*((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))\s*" 38 | ).unwrap(); 39 | // 匹配"[IPv6]:PORT" 40 | pub static ref IPV6_PORT_BRACKET_REGEX: Regex = Regex::new( 41 | r"^\s*\[([0-9a-fA-F:.]+)\]:(\d+)\s*$" 42 | ).unwrap(); 43 | // 匹配"IPv6,PORT"(逗号左右可以零个以上的空格) 44 | pub static ref IPV6_PORT_COMMA_REGEX: Regex = Regex::new( 45 | r"([0-9a-fA-F:]+:[0-9a-fA-F:]+)\s*,\s*(\d+)" 46 | ).unwrap(); 47 | } 48 | 49 | /// csv文件列名的映射关系,特别是奇奇怪怪的列名 50 | pub fn create_field_map() -> HashMap<&'static str, Vec<&'static str>> { 51 | // 可能的字段映射关系,以列名作为键,其它别名的列名作为值(向量) 52 | let mut field_map: HashMap<&str, Vec<&str>> = HashMap::new(); 53 | field_map.insert("addr", vec!["IP", "IP地址", "IP 地址", "网络地址"]); 54 | field_map.insert("port", vec!["PORT", "端口"]); 55 | field_map.insert("colo", vec!["colo", "iata", "数据中心"]); 56 | field_map.insert("loc", vec!["cca2", "alpha-2", "Country Code", "CountryCode", "国家代码"]); 57 | field_map.insert("region", vec!["region", "区域", "地区"]); 58 | field_map.insert("city", vec!["city", "城市"]); 59 | field_map 60 | } 61 | 62 | fn process_csv(filename: &str, default_port: u16) -> Result, Box> { 63 | let file = File::open(filename)?; 64 | let mut rdr = ReaderBuilder::new().from_reader(file); 65 | 66 | // 读取文件头 67 | let headers = rdr.headers()?; 68 | 69 | // csv文件列名的映射关系,特别是奇奇怪怪的列名 70 | let field_map = create_field_map(); 71 | 72 | // 尝试从标题中查找列索引(下标) 73 | let find_index = |key: &str| { 74 | field_map.get(key).and_then(|candidates| 75 | candidates.iter().find_map(|&field| 76 | headers.iter().position( 77 | |header| header.trim().to_lowercase() == field.trim().to_lowercase() // 忽略字段中的大小写 78 | ) 79 | ) 80 | ) 81 | }; 82 | // 找csv标题的列名跟向量中哪个元素对应 => 在哪个索引(下标)中 83 | let addr_index = find_index("addr"); 84 | let port_index = find_index("port"); 85 | let colo_index = find_index("colo"); 86 | let loc_index = find_index("loc"); 87 | let region_index = find_index("region"); 88 | let city_index = find_index("city"); 89 | 90 | let mut result: Vec = Vec::new(); 91 | 92 | for record in rdr.records() { 93 | let record = record?; 94 | 95 | // 获取`IP地址`字段的值 96 | let addr_column = addr_index.and_then(|index| record.get(index)).unwrap_or(""); 97 | 98 | if addr_column.is_empty() { 99 | continue; 100 | } 101 | 102 | // 获取`端口`字段的值 103 | let port_column: u16 = port_index 104 | .and_then(|index| record.get(index).and_then(|val| val.parse::().ok())) // 显示转换 105 | .unwrap_or(default_port); // 默认为`default_port` 106 | 107 | // 定义一个闭包来处理列的提取逻辑(只支持String数据类型的数据提取) 108 | let get_column_string = |index: Option| { 109 | index 110 | .and_then(|idx| record.get(idx).and_then(|val| val.parse().ok())) // 隐式转换 111 | .unwrap_or_else(|| "".to_string()) // 默认为空字符串 112 | }; 113 | 114 | // 使用闭包提取列数据,没有找到对应的列时,返回空字符串 115 | let colo_column = get_column_string(colo_index); 116 | let loc_column = get_column_string(loc_index); 117 | let region_column = get_column_string(region_index); 118 | let city_column = get_column_string(city_index); 119 | 120 | let data = FileData { 121 | addr: addr_column.to_string(), 122 | port: Some(port_column), 123 | colo: Some(colo_column), 124 | loc: Some(loc_column), 125 | region: Some(region_column), 126 | city: Some(city_column), 127 | }; 128 | result.push(data); 129 | } 130 | 131 | Ok(result) 132 | } 133 | 134 | fn process_txt(filename: &str, default_port: u16) -> Result, Box> { 135 | // 排除不需要的txt文件,注意:如果传入完整的路径,该功能不起作用 136 | if filename.starts_with("ips-v") || filename.starts_with("ipv") { 137 | return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Skipping this file"))); 138 | } 139 | let file = File::open(filename)?; 140 | let reader = BufReader::new(file); 141 | 142 | let mut seen_lines: Vec = Vec::new(); 143 | let mut result: Vec = Vec::new(); 144 | 145 | for line in reader.lines() { 146 | let line = line?; 147 | let trimmed_line = line.trim().to_string(); 148 | 149 | let contains_bool = trimmed_line.contains("/") || seen_lines.contains(&trimmed_line); 150 | if trimmed_line.is_empty() || contains_bool { 151 | continue; 152 | } 153 | 154 | // 提取地址和端口 155 | let parts: Vec = if 156 | let Some(captures) = IPV6_PORT_COMMA_REGEX.captures(&trimmed_line) 157 | { 158 | // 判断是否为 "IPv6, PORT" 格式(逗号左右,可以0个以上的空格) 159 | let ipv6 = captures.get(1).map_or("", |m| m.as_str()); 160 | let port = captures.get(2).map_or("", |m| m.as_str()); 161 | vec![format!("[{}]", ipv6), port.to_string()] 162 | } else if IPV6_PORT_SPACE_REGEX.is_match(&trimmed_line) { 163 | // 判断是否为 "IPv6 PORT" 地址 164 | SPACE_REGEX.splitn(&trimmed_line, 2) 165 | .map(|s| { 166 | let str_s = s.to_string(); 167 | let colon_count = str_s 168 | .chars() 169 | .filter(|&c| c == ':') 170 | .count(); 171 | if colon_count > 1 { 172 | if str_s.starts_with('[') && str_s.ends_with(']') { 173 | str_s // 已经有方括号,直接返回 174 | } else { 175 | format!("[{}]", str_s) // 添加方括号 176 | } 177 | } else { 178 | str_s // 不满足条件,直接返回 179 | } 180 | }) 181 | .collect() 182 | } else if let Some(captures) = IPV6_PORT_BRACKET_REGEX.captures(&trimmed_line) { 183 | // 判断是否为 "[IPv6]:PORT" 格式 184 | vec![ 185 | format!("[{}]", captures.get(1).unwrap().as_str().to_string()), 186 | captures.get(2).unwrap().as_str().to_string() 187 | ] 188 | } else if let Some(captures) = IPV4_PORT_SPACE_REGEX.captures(&trimmed_line) { 189 | // 判断是否为 "IPv4 PORT" 格式 190 | vec![ 191 | captures.get(1).unwrap().as_str().to_string(), 192 | captures.get(2).unwrap().as_str().to_string() 193 | ] 194 | } else if 195 | trimmed_line.contains(':') && 196 | trimmed_line 197 | .chars() 198 | .filter(|&c| c == ':') 199 | .count() == 1 200 | { 201 | // 判断是否为 "IPv4:PORT" 或 "Domain:PORT" 格式 202 | trimmed_line 203 | .splitn(2, ':') 204 | .map(|s| s.to_string()) 205 | .collect() 206 | } else if trimmed_line.contains(", ") { 207 | // 判断是否为 "IPv4, PORT" 、"[IPv6], PORT"、" "Domain, PORT" 格式 208 | trimmed_line 209 | .splitn(2, ", ") 210 | .map(|s| s.to_string()) 211 | .collect() 212 | } else if trimmed_line.contains(',') { 213 | // 判断是否为 "IPv4,PORT" 、"[IPv6],PORT"、" "Domain,PORT" 格式 214 | trimmed_line 215 | .splitn(2, ',') 216 | .map(|s| s.to_string()) 217 | .collect() 218 | } else if SPACE_REGEX.is_match(&trimmed_line) { 219 | // 判断是否为 "[IPv6] PORT" 或 "Domain PORT" 格式 220 | let value = SPACE_REGEX.splitn(&trimmed_line, 2) 221 | .map(|s| s.to_string()) 222 | .collect(); 223 | value 224 | } else { 225 | // 匹配 "IPv4"、"[ipv6]"、"Domain" 格式 226 | vec![trimmed_line.to_string(), default_port.to_string()] 227 | }; 228 | 229 | if parts.len() == 2 { 230 | let final_line = format!("{}:{}", parts[0], parts[1]); 231 | if !seen_lines.contains(&final_line) && !parts[0].is_empty() { 232 | let data = FileData { 233 | addr: parts[0].clone(), 234 | port: parts[1].parse::().ok(), 235 | ..Default::default() // 其它字段不管,使用默认值 236 | }; 237 | seen_lines.push(final_line); 238 | result.push(data); 239 | } 240 | } else { 241 | println!("不支持提取 `{}` 的地址和端口!", trimmed_line); 242 | } 243 | } 244 | 245 | Ok(result) 246 | } 247 | 248 | fn process_file(filename: &str, default_port: u16) -> Result, Box> { 249 | let path = Path::new(filename); 250 | let extension = path.extension().and_then(|s| s.to_str()); 251 | 252 | match extension { 253 | Some("csv") => process_csv(filename, default_port), 254 | Some("txt") => process_txt(filename, default_port), 255 | _ => Err("不支持的文件类型".into()), 256 | } 257 | } 258 | 259 | pub fn process_files_data( 260 | field_column: &str, 261 | default_port: u16, 262 | count: usize, 263 | target_path: &str 264 | ) -> Vec { 265 | let mut results: Vec = Vec::new(); // 存储结果 266 | let mut seen_addr: Vec = Vec::new(); // 数据去重 267 | 268 | let path = Path::new(target_path); 269 | if path.is_file() && path.extension().map_or(false, |ext| (ext == "txt" || ext == "csv")) { 270 | // 如果路径是 TXT 或 CSV 文件,直接处理该文件 271 | match process_file(target_path, default_port) { 272 | Ok(data) => { 273 | for item in &data { 274 | let addr: String = item.addr.clone(); 275 | let port: u16 = item.port.unwrap_or(default_port); 276 | let addr_port = format!("{}:{}", addr, port); 277 | 278 | // 数据去重,确保获取到数据没有重复的 279 | if seen_addr.contains(&addr_port) { 280 | continue; 281 | } else { 282 | seen_addr.push(addr_port.clone()); 283 | } 284 | 285 | // 获取某个字段值作为节点的别名前缀使用,注意,找不到对应的字段,则默认为空值,后面需要做处理 286 | let alias_prefix = match field_column { 287 | "colo" => item.colo.clone(), 288 | "loc" => item.loc.clone(), 289 | "region" => item.region.clone(), 290 | "city" => item.city.clone(), 291 | _ => Some("".to_string()), 292 | }; 293 | 294 | // (选择性)将需要的字段值,以MyData结构体形式存储 295 | let data = MyData { 296 | addr: addr.clone(), 297 | port: Some(port), 298 | alias: alias_prefix, 299 | }; 300 | 301 | // 如果结果数量小于指定的数量,则添加数据,否则就返回,避免无意义的IO操作(读取数据) 302 | if results.len() < count { 303 | results.push(data.clone()); 304 | } else { 305 | return results; // 直接返回结果,因为是处理单个文件 306 | } 307 | } 308 | } 309 | Err(e) => eprintln!("处理文件 `{}` 出错: {}", target_path, e), 310 | } 311 | } else if path.is_dir() { 312 | // 如果路径是文件夹,执行原来的逻辑 313 | let msg = format!("文件夹 {} 不存在!", target_path); 314 | let entries = fs::read_dir(target_path).expect(&msg); 315 | 'outer: for entry in entries { 316 | let entry = entry.unwrap(); 317 | let path = entry.path(); 318 | 319 | let is_txt_or_csv = path 320 | .extension() 321 | .map_or(false, |ext| (ext == "txt" || ext == "csv")); 322 | 323 | // 只处理txt和csv文件,process_file函数中,含有排除的txt文件 324 | if path.is_file() && is_txt_or_csv { 325 | let file_name = path.file_name().unwrap().to_string_lossy(); 326 | let filename: String = format!("{}/{}", target_path, file_name); 327 | match process_file(&filename, default_port) { 328 | Ok(data) => { 329 | for item in &data { 330 | let addr: String = item.addr.clone(); 331 | let port: u16 = item.port.unwrap_or(default_port); 332 | let addr_port = format!("{}:{}", addr, port); 333 | 334 | // 确保数据的唯一性(如果读取多文件,可能不同的文件,拥有相同的数据) 335 | if seen_addr.contains(&addr_port) { 336 | continue; 337 | } else { 338 | seen_addr.push(addr_port.clone()); 339 | } 340 | 341 | // 获取某个字段值作为节点的别名前缀使用,注意,找不到对应的字段,则默认为空值,后面需要做处理 342 | let alias_prefix = match field_column { 343 | "colo" => item.colo.clone(), // 数据中心(3个字母) 344 | "loc" => item.loc.clone(), // 国家代码(2个字母) 345 | "region" => item.region.clone(), // 地区 346 | "city" => item.city.clone(), // 城市 347 | _ => Some("".to_string()), 348 | }; 349 | 350 | // (选择性)将需要的字段值,以MyData结构体形式存储 351 | let data = MyData { 352 | addr: addr.clone(), 353 | port: Some(port), 354 | alias: alias_prefix, 355 | }; 356 | 357 | // 获取足够的数据,就停止for循环 358 | if results.len() < count { 359 | results.push(data.clone()); 360 | } else { 361 | break 'outer; 362 | } 363 | } 364 | } 365 | Err(e) => eprintln!("处理文件 `{}` 出错: {}", filename, e), 366 | } 367 | } 368 | } 369 | } else { 370 | eprintln!("路径 {} 不是有效的文件或文件夹。", target_path); 371 | } 372 | 373 | results 374 | } 375 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clash; 2 | pub mod config; 3 | pub mod convert; 4 | pub mod file_data; 5 | pub mod net_data; 6 | pub mod qrcode; 7 | pub mod singbox; 8 | pub mod v2ray; 9 | pub mod build; 10 | -------------------------------------------------------------------------------- /src/utils/net_data.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::file_data::{ self, FileData, MyData }; 2 | use reqwest; 3 | use csv::ReaderBuilder; 4 | use crossbeam_channel::unbounded; 5 | use std::{ error::Error, thread }; 6 | 7 | // 同步函数中使用异步,使用 std::thread::spawn 在另一个线程中运行异步代码 8 | fn read_csv_from_url( 9 | url: &str, 10 | default_port: u16 11 | ) -> Result, Box> { 12 | let (sender, receiver) = unbounded(); 13 | let url_copy = url.to_string(); 14 | 15 | thread::spawn(move || { 16 | let rt = tokio::runtime::Runtime::new().unwrap(); 17 | let result = rt.block_on(async { 18 | let response = reqwest::get(&url_copy).await?; 19 | if !response.status().is_success() { 20 | return Err(format!("Failed to fetch CSV from URL: {}", response.status()).into()); 21 | } 22 | let body = response.text().await?; 23 | let mut rdr = ReaderBuilder::new().from_reader(body.as_bytes()); 24 | let headers = rdr.headers()?; // 读取并忽略头部 25 | 26 | // csv文件列名的映射关系,特别是奇奇怪怪的列名 27 | let field_map = file_data::create_field_map(); 28 | 29 | // 尝试从标题中查找列索引(下标) 30 | let find_index = |key: &str| { 31 | field_map.get(key).and_then(|candidates| 32 | candidates.iter().find_map(|&field| 33 | headers.iter().position( 34 | |header| header.trim().to_lowercase() == field.trim().to_lowercase() // 忽略字段中的大小写 35 | ) 36 | ) 37 | ) 38 | }; 39 | // 找csv标题的列名跟向量中哪个元素对应 => 在哪个索引(下标)中 40 | let addr_index = find_index("addr"); 41 | let port_index = find_index("port"); 42 | let colo_index = find_index("colo"); 43 | let loc_index = find_index("loc"); 44 | let region_index = find_index("region"); 45 | let city_index = find_index("city"); 46 | 47 | // 1. 将 CSV 记录转换为结构体实例,并收集到向量 48 | let mut records: Vec = Vec::new(); 49 | 50 | for record in rdr.records() { 51 | let record = record?; 52 | 53 | // 获取`IP地址`字段的值 54 | let addr_column = addr_index.and_then(|index| record.get(index)).unwrap_or(""); 55 | 56 | if addr_column.is_empty() { 57 | continue; 58 | } 59 | 60 | // 获取`端口`字段的值 61 | let port_column: u16 = port_index 62 | .and_then(|index| record.get(index).and_then(|val| val.parse::().ok())) // 显示转换 63 | .unwrap_or(default_port); // 默认为`default_port` 64 | 65 | // 定义一个闭包来处理列的提取逻辑(只支持String数据类型的数据提取) 66 | let get_column_string = |index: Option| { 67 | index 68 | .and_then(|idx| record.get(idx).and_then(|val| val.parse().ok())) // 隐式转换 69 | .unwrap_or_else(|| "".to_string()) // 默认为空字符串 70 | }; 71 | 72 | // 使用闭包提取列数据,没有找到对应的列时,返回空字符串 73 | let colo_column = get_column_string(colo_index); 74 | let loc_column = get_column_string(loc_index); 75 | let region_column = get_column_string(region_index); 76 | let city_column = get_column_string(city_index); 77 | 78 | let data = FileData { 79 | addr: addr_column.to_string(), 80 | port: Some(port_column), 81 | colo: Some(colo_column), 82 | loc: Some(loc_column), 83 | region: Some(region_column), 84 | city: Some(city_column), 85 | }; 86 | records.push(data); 87 | } 88 | Ok(records) // 2. 返回向量,这个类似return 89 | }); 90 | sender.send(result).unwrap(); // 3. 这里将这个 result 发送到通道中 91 | }); 92 | 93 | // 4. 接收通道中的结果 94 | receiver.recv().unwrap() 95 | } 96 | 97 | fn read_txt_from_url( 98 | url: &str, 99 | default_port: u16 100 | ) -> Result, Box> { 101 | let (sender, receiver) = unbounded(); 102 | let url_copy = url.to_string(); 103 | let mut seen_lines: Vec = Vec::new(); 104 | thread::spawn(move || { 105 | let rt = tokio::runtime::Runtime::new().unwrap(); 106 | let result = rt.block_on(async { 107 | let response = reqwest::get(&url_copy).await?; 108 | if !response.status().is_success() { 109 | return Err(format!("Failed to fetch txt from URL: {}", response.status()).into()); 110 | } 111 | let body = response.text().await?; 112 | let mut records: Vec = Vec::new(); 113 | for line in body.lines() { 114 | let trimmed_line = line.trim().to_string(); 115 | 116 | let contains_bool = 117 | trimmed_line.contains("/") || seen_lines.contains(&trimmed_line); 118 | if trimmed_line.is_empty() || contains_bool { 119 | continue; 120 | } 121 | 122 | // 提取地址和端口 123 | let parts: Vec = if 124 | let Some(captures) = file_data::IPV6_PORT_COMMA_REGEX.captures(&trimmed_line) 125 | { 126 | // 判断是否为 "IPv6, PORT" 格式(逗号左右,可以0个以上的空格) 127 | let ipv6 = captures.get(1).map_or("", |m| m.as_str()); 128 | let port = captures.get(2).map_or("", |m| m.as_str()); 129 | vec![format!("[{}]", ipv6), port.to_string()] 130 | } else if file_data::IPV6_PORT_SPACE_REGEX.is_match(&trimmed_line) { 131 | // 判断是否为 "IPv6 PORT" 地址 132 | file_data::SPACE_REGEX 133 | .splitn(&trimmed_line, 2) 134 | .map(|s| { 135 | let str_s = s.to_string(); 136 | let colon_count = str_s 137 | .chars() 138 | .filter(|&c| c == ':') 139 | .count(); 140 | if colon_count > 1 { 141 | if str_s.starts_with('[') && str_s.ends_with(']') { 142 | str_s // 已经有方括号,直接返回 143 | } else { 144 | format!("[{}]", str_s) // 添加方括号 145 | } 146 | } else { 147 | str_s // 不满足条件,直接返回 148 | } 149 | }) 150 | .collect() 151 | } else if 152 | let Some(captures) = file_data::IPV6_PORT_BRACKET_REGEX.captures(&trimmed_line) 153 | { 154 | // 判断是否为 "[IPv6]:PORT" 格式 155 | vec![ 156 | format!("[{}]", captures.get(1).unwrap().as_str().to_string()), 157 | captures.get(2).unwrap().as_str().to_string() 158 | ] 159 | } else if 160 | let Some(captures) = file_data::IPV4_PORT_SPACE_REGEX.captures(&trimmed_line) 161 | { 162 | // 判断是否为 "IPv4 PORT" 格式 163 | vec![ 164 | captures.get(1).unwrap().as_str().to_string(), 165 | captures.get(2).unwrap().as_str().to_string() 166 | ] 167 | } else if 168 | trimmed_line.contains(':') && 169 | trimmed_line 170 | .chars() 171 | .filter(|&c| c == ':') 172 | .count() == 1 173 | { 174 | // 判断是否为 "IPv4:PORT" 或 "Domain:PORT" 格式 175 | trimmed_line 176 | .splitn(2, ':') 177 | .map(|s| s.to_string()) 178 | .collect() 179 | } else if trimmed_line.contains(", ") { 180 | // 判断是否为 "IPv4, PORT" 、"[IPv6], PORT"、" "Domain, PORT" 格式 181 | trimmed_line 182 | .splitn(2, ", ") 183 | .map(|s| s.to_string()) 184 | .collect() 185 | } else if trimmed_line.contains(',') { 186 | // 判断是否为 "IPv4,PORT" 、"[IPv6],PORT"、" "Domain,PORT" 格式 187 | trimmed_line 188 | .splitn(2, ',') 189 | .map(|s| s.to_string()) 190 | .collect() 191 | } else if file_data::SPACE_REGEX.is_match(&trimmed_line) { 192 | // 判断是否为 "[IPv6] PORT" 或 "Domain PORT" 格式 193 | let value = file_data::SPACE_REGEX 194 | .splitn(&trimmed_line, 2) 195 | .map(|s| s.to_string()) 196 | .collect(); 197 | value 198 | } else { 199 | // 匹配 "IPv4"、"[ipv6]"、"Domain" 格式 200 | vec![trimmed_line.to_string(), default_port.to_string()] 201 | }; 202 | 203 | if parts.len() == 2 { 204 | let final_line = format!("{}:{}", parts[0], parts[1]); 205 | if !seen_lines.contains(&final_line) && !parts[0].is_empty() { 206 | let data = FileData { 207 | addr: parts[0].clone(), 208 | port: parts[1].parse::().ok(), 209 | ..Default::default() // 其它字段不管,使用默认值 210 | }; 211 | seen_lines.push(final_line); 212 | records.push(data); 213 | } 214 | } else { 215 | println!("不支持提取 `{}` 的地址和端口!", trimmed_line); 216 | } 217 | } 218 | Ok(records) 219 | }); 220 | sender.send(result).unwrap(); 221 | }); 222 | 223 | receiver.recv().unwrap() 224 | } 225 | 226 | fn process_url( 227 | url: &str, 228 | default_port: u16 229 | ) -> Result, Box> { 230 | match url { 231 | url if url.to_lowercase().ends_with(".txt") => read_txt_from_url(url, default_port), 232 | url if url.to_lowercase().ends_with(".csv") => read_csv_from_url(url, default_port), 233 | _ => Err(format!("{} 不是 txt 或 csv 文件的链接", url).into()), 234 | } 235 | } 236 | 237 | pub fn process_network_data( 238 | field_column: &str, 239 | default_port: u16, 240 | count: usize, 241 | url: &str 242 | ) -> Vec { 243 | let mut results: Vec = Vec::new(); // 存储结果 244 | let mut seen_addr = Vec::new(); 245 | 246 | if url.to_lowercase().starts_with("https://") { 247 | match process_url(url, default_port) { 248 | Ok(data) => { 249 | for item in &data { 250 | let addr: String = item.addr.clone(); 251 | let port: u16 = item.port.unwrap_or(default_port); 252 | let addr_port = format!("{}:{}", addr, port); 253 | 254 | // 数据去重,确保获取到数据没有重复的 255 | if seen_addr.contains(&addr_port) { 256 | continue; 257 | } else { 258 | seen_addr.push(addr_port.clone()); 259 | } 260 | 261 | // 获取某个字段值作为节点的别名前缀使用,注意,找不到对应的字段,则默认为空值,后面需要做处理 262 | let alias_prefix = match field_column { 263 | "colo" => item.colo.clone(), 264 | "loc" => item.loc.clone(), 265 | "region" => item.region.clone(), 266 | "city" => item.city.clone(), 267 | _ => Some("".to_string()), 268 | }; 269 | 270 | // (选择性)将需要的字段值,以MyData结构体形式存储 271 | let data = MyData { 272 | addr: addr.clone(), 273 | port: Some(port), 274 | alias: alias_prefix, 275 | }; 276 | 277 | // 如果结果数量小于指定的数量,则添加数据,否则就返回,避免无意义的IO操作(读取数据) 278 | if results.len() < count { 279 | results.push(data.clone()); 280 | } else { 281 | break; 282 | } 283 | } 284 | } 285 | Err(e) => eprintln!("Error: {}", e), 286 | } 287 | } 288 | 289 | results 290 | } 291 | -------------------------------------------------------------------------------- /src/utils/qrcode.rs: -------------------------------------------------------------------------------- 1 | use base64::encode; 2 | use image::{ ImageFormat, Luma }; 3 | use qrcode::QrCode; 4 | use std::io::Cursor; 5 | /// 生成包含二维码和内容的 HTML 页面 6 | pub fn generate_html_with_qrcode(content: &str, url: &str) -> String { 7 | let code = QrCode::new(url).unwrap(); 8 | // 渲染二维码为图像并转换为Base64字符串 9 | let image = code.render::>().build(); 10 | let mut buffer = Cursor::new(Vec::new()); 11 | image.write_to(&mut buffer, ImageFormat::Png).unwrap(); 12 | let base64_qrcode = encode(buffer.get_ref()); 13 | 14 | // 构建HTML内容 15 | format!( 16 | r#" 17 |
{}
18 |
19 |

扫描以下二维码以便在手机上查看:

20 | 21 | "#, 22 | content, 23 | base64_qrcode 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/singbox.rs: -------------------------------------------------------------------------------- 1 | use super::config::{get_yaml_value, get_yaml_value_with_fallback}; 2 | use serde_json::{json, Value as JsonValue}; 3 | use serde_yaml::Value as YamlValue; 4 | use std::collections::HashMap; 5 | 6 | pub fn build_singbox_config_json( 7 | proxy_type: &str, 8 | yaml_value: &mut YamlValue, 9 | remarks: String, 10 | server_address: String, 11 | server_port: u16, 12 | ) -> (String, String) { 13 | match proxy_type { 14 | "vless" => { 15 | let (remarks_name, vless_singbox) = 16 | build_vless_singbox_config(yaml_value, remarks, server_address, server_port); 17 | return (remarks_name, vless_singbox); 18 | } 19 | "trojan" => { 20 | let (remarks_name, trojan_singbox) = 21 | build_trojan_singbox_config(yaml_value, remarks, server_address, server_port); 22 | return (remarks_name, trojan_singbox); 23 | } 24 | "ss" => { 25 | let (remarks_name, ss_singbox) = 26 | build_ss_singbox_config(yaml_value, remarks, server_address, server_port); 27 | return (remarks_name, ss_singbox); 28 | } 29 | _ => {} 30 | } 31 | 32 | return (String::new(), String::new()); 33 | } 34 | 35 | fn build_ss_singbox_config( 36 | yaml_value: &mut YamlValue, 37 | remarks: String, 38 | server_address: String, 39 | server_port: u16, 40 | ) -> (String, String) { 41 | let path = get_yaml_value(&yaml_value, &["plugin-opts", "path"]) 42 | .and_then(|v| v.as_str()) 43 | .unwrap_or_default(); 44 | let host = get_yaml_value(&yaml_value, &["plugin-opts", "host"]) 45 | .and_then(|v| v.as_str()) 46 | .unwrap_or_default(); 47 | let password = get_yaml_value(&yaml_value, &["password"]) 48 | .and_then(|v| v.as_str()) 49 | .unwrap_or_default(); 50 | let tls_val = get_yaml_value(&yaml_value, &["plugin-opts", "tls"]) 51 | .and_then(|v| v.as_bool()) 52 | .unwrap_or(true); 53 | 54 | let insert_tls_str = match tls_val { 55 | true => format!("tls;"), 56 | false => format!(""), 57 | }; 58 | let plugin_value = format!( 59 | "{}mux=0;mode=websocket;path={};host={}", 60 | insert_tls_str, path, host 61 | ); 62 | 63 | let singbox_ss_json_str = r#"{ 64 | "type": "shadowsocks", 65 | "tag": "", 66 | "server": "", 67 | "server_port": 443, 68 | "method": "none", 69 | "password": "", 70 | "plugin": "v2ray-plugin", 71 | "plugin_opts": "" 72 | }"#; 73 | 74 | let mut ss_jsonvalue: JsonValue = serde_json::from_str(singbox_ss_json_str).unwrap_or_default(); 75 | 76 | ss_jsonvalue["server"] = json!(server_address); 77 | ss_jsonvalue["server_port"] = json!(server_port); 78 | ss_jsonvalue["password"] = json!(password); 79 | ss_jsonvalue["plugin_opts"] = json!(plugin_value); 80 | ss_jsonvalue["tag"] = json!(remarks.clone()); 81 | 82 | let json_string = serde_json::to_string_pretty(&ss_jsonvalue).unwrap_or_default(); 83 | 84 | return (remarks, json_string); 85 | } 86 | 87 | fn build_vless_singbox_config( 88 | yaml_value: &mut YamlValue, 89 | remarks: String, 90 | server_address: String, 91 | server_port: u16, 92 | ) -> (String, String) { 93 | let uuid = get_yaml_value(&yaml_value, &["uuid"]) 94 | .and_then(|v| v.as_str()) 95 | .unwrap_or_default(); 96 | let client_fingerprint = get_yaml_value(&yaml_value, &["client-fingerprint"]) 97 | .and_then(|v| v.as_str()) 98 | .unwrap_or_default(); 99 | let path = get_yaml_value(&yaml_value, &["ws-opts", "path"]) 100 | .and_then(|v| v.as_str()) 101 | .unwrap_or_default(); 102 | let host = get_yaml_value(&yaml_value, &["ws-opts", "headers", "Host"]) 103 | .and_then(|v| v.as_str()) 104 | .unwrap_or_default(); 105 | 106 | let tls_server_name = 107 | get_yaml_value_with_fallback(&yaml_value, &["servername", "sni"]).unwrap_or_default(); 108 | 109 | let vless_singbox_config = r#"{ 110 | "type": "vless", 111 | "tag": "vless_tag", 112 | "server": "", 113 | "server_port": 443, 114 | "uuid": "", 115 | "network": "tcp", 116 | "tls": { 117 | "enabled": true, 118 | "server_name": "", 119 | "insecure": true, 120 | "utls": { 121 | "enabled": true, 122 | "fingerprint": "chrome" 123 | } 124 | }, 125 | "transport": { 126 | "type": "ws", 127 | "path": "/", 128 | "headers": {"Host": ""}, 129 | "early_data_header_name": "Sec-WebSocket-Protocol" 130 | } 131 | }"#; 132 | 133 | let mut jsonvalue: JsonValue = serde_json::from_str(vless_singbox_config).unwrap_or_default(); 134 | 135 | let outer_updates = HashMap::from([ 136 | ("tag", json!(remarks)), 137 | ("server", json!(server_address)), 138 | ("server_port", json!(server_port)), 139 | ("uuid", json!(uuid)), 140 | ]); 141 | 142 | let result: JsonValue = update_singbox_json_value( 143 | &mut jsonvalue, 144 | outer_updates, 145 | host.to_string(), 146 | path.to_string(), 147 | tls_server_name.to_string(), 148 | client_fingerprint.to_string(), 149 | ); 150 | 151 | let json_string = serde_json::to_string_pretty(&result).unwrap_or_default(); 152 | 153 | return (remarks, json_string); 154 | } 155 | 156 | fn build_trojan_singbox_config( 157 | yaml_value: &mut YamlValue, 158 | remarks: String, 159 | server_address: String, 160 | server_port: u16, 161 | ) -> (String, String) { 162 | let password = get_yaml_value(&yaml_value, &["password"]) 163 | .and_then(|v| v.as_str()) 164 | .unwrap_or_default(); 165 | let client_fingerprint = get_yaml_value(&yaml_value, &["client-fingerprint"]) 166 | .and_then(|v| v.as_str()) 167 | .unwrap_or_default(); 168 | let path = get_yaml_value(&yaml_value, &["ws-opts", "path"]) 169 | .and_then(|v| v.as_str()) 170 | .unwrap_or_default(); 171 | let host = get_yaml_value(&yaml_value, &["ws-opts", "headers", "Host"]) 172 | .and_then(|v| v.as_str()) 173 | .unwrap_or_default(); 174 | 175 | let tls_server_name = 176 | get_yaml_value_with_fallback(&yaml_value, &["sni", "servername"]).unwrap_or_default(); 177 | 178 | let trojan_singbox_config = r#"{ 179 | "type": "trojan", 180 | "tag": "tag_name", 181 | "server": "", 182 | "server_port": 443, 183 | "password": "", 184 | "network": "tcp", 185 | "tls": { 186 | "enabled": true, 187 | "server_name": "", 188 | "insecure": true, 189 | "utls": { 190 | "enabled": true, 191 | "fingerprint": "chrome" 192 | } 193 | }, 194 | "transport": { 195 | "type": "ws", 196 | "path": "/", 197 | "headers": {"Host": ""}, 198 | "early_data_header_name": "Sec-WebSocket-Protocol" 199 | } 200 | }"#; 201 | 202 | let mut jsonvalue: JsonValue = serde_json::from_str(trojan_singbox_config).unwrap_or_default(); 203 | 204 | let outer_updates = HashMap::from([ 205 | ("tag", json!(remarks)), 206 | ("server", json!(server_address)), 207 | ("server_port", json!(server_port)), 208 | ("password", json!(password)), 209 | ]); 210 | 211 | let result: JsonValue = update_singbox_json_value( 212 | &mut jsonvalue, 213 | outer_updates, 214 | host.to_string(), 215 | path.to_string(), 216 | tls_server_name.to_string(), 217 | client_fingerprint.to_string(), 218 | ); 219 | 220 | let json_string = serde_json::to_string_pretty(&result).unwrap_or_default(); 221 | 222 | return (remarks, json_string); 223 | } 224 | 225 | fn update_singbox_json_value( 226 | jsonvalue: &mut JsonValue, 227 | outer_updates: HashMap<&str, JsonValue>, 228 | host: String, 229 | path: String, 230 | tls_server_name: String, 231 | client_fingerprint: String, 232 | ) -> JsonValue { 233 | // 修改jsonvalue的外层字段(多个字段) 234 | for (key, new_value) in outer_updates { 235 | if let Some(outer_value) = jsonvalue.get_mut(key) { 236 | *outer_value = new_value; 237 | } 238 | } 239 | // 修改jsonvalue的tls字段 240 | if let Some(tls) = jsonvalue.get_mut("tls") { 241 | if let Some(server_name) = tls.get_mut("server_name") { 242 | *server_name = json!(tls_server_name); 243 | } 244 | // 手动关闭tls 245 | if host.ends_with("workers.dev") { 246 | if let Some(tls_enabled) = tls.get_mut("enabled") { 247 | *tls_enabled = json!(false); 248 | } 249 | } 250 | if let Some(utls) = tls.get_mut("utls") { 251 | if let Some(fingerprint) = utls.get_mut("fingerprint") { 252 | *fingerprint = json!(client_fingerprint); 253 | } 254 | } 255 | } 256 | // 修改jsonvalue的transport字段 257 | if let Some(transport) = jsonvalue.get_mut("transport") { 258 | if let Some(path_value) = transport.get_mut("path") { 259 | *path_value = json!(path); 260 | } 261 | if let Some(headers) = transport.get_mut("headers") { 262 | if let Some(host_value) = headers.get_mut("Host") { 263 | *host_value = json!(host); 264 | } 265 | } 266 | } 267 | 268 | jsonvalue.clone() 269 | } 270 | -------------------------------------------------------------------------------- /src/utils/v2ray.rs: -------------------------------------------------------------------------------- 1 | use super::config::{get_yaml_value, get_yaml_value_with_fallback}; 2 | use serde_qs as qs; 3 | use serde_yaml::Value as YamlValue; 4 | use std::collections::BTreeMap; 5 | 6 | pub fn build_v2ray_links( 7 | proxy_type: &str, 8 | yaml_value: &mut YamlValue, 9 | remarks: String, 10 | server_address: String, 11 | server_port: u16, 12 | ) -> (String, String) { 13 | match proxy_type { 14 | "vless" => { 15 | let vless_link = 16 | build_vless_link(yaml_value, remarks.clone(), server_address, server_port); 17 | return (remarks, vless_link); // 前面是节点名称,后面是节点配置 18 | } 19 | "trojan" => { 20 | let trojan_link = 21 | build_trojan_linnk(yaml_value, remarks.clone(), server_address, server_port); 22 | return (remarks, trojan_link); // 前面是节点名称,后面是节点配置 23 | } 24 | "ss" => { 25 | let ss_link = build_ss_link(yaml_value, remarks.clone(), server_address, server_port); 26 | return (remarks, ss_link); // 前面是节点名称,后面是节点配置 27 | } 28 | _ => {} 29 | } 30 | return ("".to_string(), "".to_string()); 31 | } 32 | 33 | fn build_ss_link( 34 | yaml_value: &mut YamlValue, 35 | remarks: String, 36 | server_address: String, 37 | server_port: u16, 38 | ) -> String { 39 | let path = get_yaml_value(&yaml_value, &["plugin-opts", "path"]) 40 | .and_then(|v| v.as_str()) 41 | .unwrap_or_default(); 42 | let host = get_yaml_value(&yaml_value, &["plugin-opts", "host"]) 43 | .and_then(|v| v.as_str()) 44 | .unwrap_or_default(); 45 | let tls_val = get_yaml_value(&yaml_value, &["plugin-opts", "tls"]) 46 | .and_then(|v| v.as_bool()) 47 | .unwrap_or(true); 48 | let password = get_yaml_value(&yaml_value, &["password"]) 49 | .and_then(|v| v.as_str()) 50 | .unwrap_or_default(); 51 | let base64_encoded = base64::encode(format!("none:{}", password).as_bytes()); 52 | 53 | let insert_tls_str = match tls_val { 54 | true => format!("tls;"), 55 | false => format!(""), 56 | }; 57 | 58 | let plugin = format!( 59 | "v2ray-plugin;{}mux=0;mode=websocket;path={};host={}", 60 | insert_tls_str, path, host 61 | ) 62 | .replace("=", "%3D"); 63 | 64 | let ss_link: String = format!( 65 | "ss://{}@{}:{}?plugin={}#{}", 66 | base64_encoded, server_address, server_port, plugin, remarks 67 | ); 68 | ss_link 69 | } 70 | 71 | fn build_vless_link( 72 | yaml_value: &mut YamlValue, 73 | remarks: String, 74 | server_address: String, 75 | server_port: u16, 76 | ) -> String { 77 | let uuid = get_yaml_value(&yaml_value, &["uuid"]) 78 | .and_then(|v| v.as_str()) 79 | .unwrap_or_default(); 80 | let network = get_yaml_value(&yaml_value, &["network"]) 81 | .and_then(|v| v.as_str()) 82 | .unwrap_or_default(); 83 | let client_fingerprint = get_yaml_value(&yaml_value, &["client-fingerprint"]) 84 | .and_then(|v| v.as_str()) 85 | .unwrap_or_default(); 86 | let path = get_yaml_value(&yaml_value, &["ws-opts", "path"]) 87 | .and_then(|v| v.as_str()) 88 | .unwrap_or_default(); 89 | let host = get_yaml_value(&yaml_value, &["ws-opts", "headers", "Host"]) 90 | .and_then(|v| v.as_str()) 91 | .unwrap_or_default(); 92 | 93 | let sni = get_yaml_value_with_fallback(&yaml_value, &["sni", "servername"]).unwrap_or_default(); 94 | let security = match host.ends_with("workers.dev") { 95 | true => "none", 96 | false => "tls", 97 | }; 98 | 99 | let encoding_remarks = urlencoding::encode(remarks.as_str()); 100 | 101 | let mut params = BTreeMap::new(); 102 | params.insert("encryption", "none"); 103 | params.insert("security", &security); 104 | params.insert("type", &network); 105 | params.insert("host", &host); 106 | params.insert("sni", &sni); 107 | params.insert("fp", &client_fingerprint); 108 | params.insert("allowInsecure", "1"); 109 | params.insert("path", &path); 110 | 111 | // 过滤掉值为空的键值对,然后将数据结构序列化为Query String格式的字符串 112 | let all_params_str = serialize_to_query_string(params); 113 | 114 | let vless_link = format!( 115 | "vless://{uuid}@{server_address}:{server_port}/?{all_params_str}#{encoding_remarks}" 116 | ); 117 | vless_link 118 | } 119 | 120 | fn build_trojan_linnk( 121 | yaml_value: &mut YamlValue, 122 | remarks: String, 123 | server_address: String, 124 | server_port: u16, 125 | ) -> String { 126 | let password = get_yaml_value(&yaml_value, &["password"]) 127 | .and_then(|v| v.as_str()) 128 | .unwrap_or_default(); 129 | let network = get_yaml_value(&yaml_value, &["network"]) 130 | .and_then(|v| v.as_str()) 131 | .unwrap_or_default(); 132 | let client_fingerprint = get_yaml_value(&yaml_value, &["client-fingerprint"]) 133 | .and_then(|v| v.as_str()) 134 | .unwrap_or_default(); 135 | let path = get_yaml_value(&yaml_value, &["ws-opts", "path"]) 136 | .and_then(|v| v.as_str()) 137 | .unwrap_or_default(); 138 | let host = get_yaml_value(&yaml_value, &["ws-opts", "headers", "Host"]) 139 | .and_then(|v| v.as_str()) 140 | .unwrap_or_default(); 141 | 142 | let sni = get_yaml_value_with_fallback(&yaml_value, &["sni", "servername"]).unwrap_or_default(); 143 | let security = match host.ends_with("workers.dev") { 144 | true => "none", 145 | false => "tls", 146 | }; 147 | 148 | let encoding_remarks = urlencoding::encode(&remarks); 149 | 150 | // 构建节点链接后面的参数 151 | let mut params = BTreeMap::new(); 152 | params.insert("security", security); 153 | params.insert("sni", &sni); 154 | params.insert("fp", &client_fingerprint); 155 | params.insert("type", &network); 156 | params.insert("host", &host); 157 | params.insert("allowInsecure", "1"); 158 | params.insert("path", &path); 159 | 160 | // 过滤掉值为空的键值对,然后将数据结构序列化为Query String格式的字符串 161 | let all_params_str = serialize_to_query_string(params); 162 | 163 | let trojan_link = format!( 164 | "trojan://{password}@{server_address}:{server_port}/?{all_params_str}#{encoding_remarks}" 165 | ); 166 | trojan_link 167 | } 168 | 169 | fn serialize_to_query_string(params: BTreeMap<&str, &str>) -> String { 170 | let filtered_params: BTreeMap<_, _> = 171 | params.into_iter().filter(|(_, v)| !v.is_empty()).collect(); 172 | let all_params_str = qs::to_string(&filtered_params).unwrap_or_default(); 173 | all_params_str 174 | } 175 | -------------------------------------------------------------------------------- /template/sing-box.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "level": "debug", 4 | "timestamp": true 5 | }, 6 | "dns": { 7 | "servers": [ 8 | { 9 | "tag": "proxyDns", 10 | "address": "tls://8.8.8.8", 11 | "detour": "proxy" 12 | }, 13 | { 14 | "tag": "localDns", 15 | "address": "https://223.5.5.5/dns-query", 16 | "detour": "direct" 17 | }, 18 | { 19 | "tag": "block", 20 | "address": "rcode://success" 21 | } 22 | ], 23 | "rules": [ 24 | { 25 | "domain": ["ghproxy.com", "cdn.jsdelivr.net", "testingcf.jsdelivr.net"], 26 | "server": "localDns" 27 | }, 28 | { 29 | "rule_set": "geosite-category-ads-all", 30 | "server": "block" 31 | }, 32 | { 33 | "outbound": "any", 34 | "server": "localDns", 35 | "disable_cache": true 36 | }, 37 | { 38 | "clash_mode": "direct", 39 | "server": "localDns" 40 | }, 41 | { 42 | "clash_mode": "global", 43 | "server": "proxyDns" 44 | } 45 | ], 46 | "strategy": "ipv4_only" 47 | }, 48 | "inbounds": [ 49 | { 50 | "type": "tun", 51 | "inet4_address": "172.19.0.1/30", 52 | "mtu": 9000, 53 | "auto_route": true, 54 | "strict_route": true, 55 | "sniff": true, 56 | "endpoint_independent_nat": false, 57 | "stack": "system", 58 | "platform": { 59 | "http_proxy": { 60 | "enabled": true, 61 | "server": "127.0.0.1", 62 | "server_port": 2080 63 | } 64 | } 65 | }, 66 | { 67 | "type": "mixed", 68 | "listen": "127.0.0.1", 69 | "listen_port": 2080, 70 | "sniff": true, 71 | "users": [] 72 | } 73 | ], 74 | "outbounds": [ 75 | { 76 | "tag": "proxy", 77 | "type": "selector", 78 | "outbounds": ["auto", "direct", "{all}"] 79 | }, 80 | { 81 | "tag": "auto", 82 | "type": "urltest", 83 | "outbounds": ["{all}"], 84 | "url": "http://www.gstatic.com/generate_204", 85 | "interval": "10m", 86 | "tolerance": 50 87 | }, 88 | { 89 | "type": "direct", 90 | "tag": "direct" 91 | }, 92 | { 93 | "type": "dns", 94 | "tag": "dns-out" 95 | }, 96 | { 97 | "type": "block", 98 | "tag": "block" 99 | } 100 | ], 101 | "route": { 102 | "auto_detect_interface": true, 103 | "final": "proxy", 104 | "rules": [ 105 | { 106 | "protocol": "dns", 107 | "outbound": "dns-out" 108 | }, 109 | { 110 | "network": "udp", 111 | "port": 443, 112 | "outbound": "block" 113 | }, 114 | { 115 | "rule_set": "geosite-category-ads-all", 116 | "outbound": "block" 117 | }, 118 | { 119 | "clash_mode": "direct", 120 | "outbound": "direct" 121 | }, 122 | { 123 | "clash_mode": "global", 124 | "outbound": "proxy" 125 | }, 126 | { 127 | "domain": [ 128 | "clash.razord.top", 129 | "yacd.metacubex.one", 130 | "yacd.haishan.me", 131 | "d.metacubex.one" 132 | ], 133 | "outbound": "direct" 134 | } 135 | ], 136 | "rule_set": [ 137 | { 138 | "tag": "geosite-category-ads-all", 139 | "type": "remote", 140 | "format": "binary", 141 | "url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geosite/category-ads-all.srs", 142 | "download_detour": "direct" 143 | } 144 | ] 145 | }, 146 | "experimental": { 147 | "clash_api": { 148 | "external_controller": "127.0.0.1:9090", 149 | "external_ui": "ui", 150 | "secret": "", 151 | "external_ui_download_url": "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip", 152 | "external_ui_download_detour": "", 153 | "default_mode": "rule" 154 | }, 155 | "cache_file": { 156 | "enabled": true, 157 | "store_fakeip": false 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /使用说明.txt: -------------------------------------------------------------------------------- 1 | 【YAML】本工具的功能: 2 | 3 | 批量将优选的IP或域名,写入到 Cloudflare 搭建的 vless/trojan/ss-v2ray 协议的配置节点中,并转换为 v2ray、sing-box、clash.mate/mihomo 订阅! 4 | 5 | ————————————————————————————————————————————————————————————————————————————————————————————————— 6 | 7 | web服务地址:http://127.0.0.1:10111 8 | 9 | 订阅地址格式: 10 | 11 | http://127.0.0.1:10111/sub?target=[v2ray,singbox,clash]&template=[true,false]&n=[1..?]&type=[vless,trojan,ss]&page=[1,?] 12 | &id=[1..255]&tls=[true,false]&dPort=[80..65535]&column=[colo,loc,region,city]&source=[数据来源的路径] 13 | 14 | ————————————————————————————————————————————————————————————————————————————————————————————————— 15 | 16 | 订阅URL示例: 17 | 18 | http://127.0.0.1:10111/sub 19 | http://127.0.0.1:10111/sub?target=v2ray 20 | http://127.0.0.1:10111/sub?target=singbox 21 | http://127.0.0.1:10111/sub?target=clash 22 | ——————————————————————————————————————————————————————————————————————— 23 | http://127.0.0.1:10111/sub?target=singbox&template=false 24 | http://127.0.0.1:10111/sub?target=clash&template=false 25 | 26 | http://127.0.0.1:10111/sub?target=singbox&template=false&id=1 27 | http://127.0.0.1:10111/sub?target=singbox&template=false&type=vless 28 | 29 | http://127.0.0.1:10111/sub?target=singbox&page=2 30 | http://127.0.0.1:10111/sub?target=clash&template=false&page=2 31 | ——————————————————————————————————————————————————————————————————————— 32 | http://127.0.0.1:10111/sub?target=v2ray&id=1 33 | http://127.0.0.1:10111/sub?target=singbox&id=1 34 | http://127.0.0.1:10111/sub?target=clash&id=1 35 | ——————————————————————————————————————————————————————————————————————— 36 | http://127.0.0.1:10111/sub?target=v2ray&type=vless 37 | http://127.0.0.1:10111/sub?target=v2ray&type=trojan 38 | http://127.0.0.1:10111/sub?target=v2ray&type=ss 39 | 40 | http://127.0.0.1:10111/sub?target=singbox&type=vless 41 | http://127.0.0.1:10111/sub?target=singbox&type=trojan 42 | http://127.0.0.1:10111/sub?target=singbox&type=ss 43 | 44 | http://127.0.0.1:10111/sub?target=clash&type=vless 45 | http://127.0.0.1:10111/sub?target=clash&type=trojan 46 | http://127.0.0.1:10111/sub?target=clash&type=ss 47 | ——————————————————————————————————————————————————————————————————————— 48 | http://127.0.0.1:10111/sub?target=v2ray&tls=true 49 | http://127.0.0.1:10111/sub?target=v2ray&tls=false&type=vless 50 | 51 | http://127.0.0.1:10111/sub?target=singbox&tls=true 52 | http://127.0.0.1:10111/sub?target=singbox&tls=false&type=vless 53 | 54 | http://127.0.0.1:10111/sub?target=clash&tls=true 55 | http://127.0.0.1:10111/sub?target=clash&tls=false&type=vless 56 | ——————————————————————————————————————————————————————————————————————— 57 | http://127.0.0.1:10111/sub?target=v2ray&n=500 58 | http://127.0.0.1:10111/sub?target=singbox&n=100 59 | http://127.0.0.1:10111/sub?target=clash&n=150 60 | ——————————————————————————————————————————————————————————————————————— 61 | http://127.0.0.1:10111/sub?target=v2ray&dport=443 62 | http://127.0.0.1:10111/sub?target=singbox&dport=443 63 | http://127.0.0.1:10111/sub?target=clash&dport=2053 64 | ——————————————————————————————————————————————————————————————————————— 65 | http://127.0.0.1:10111/sub?target=v2ray&column=loc 66 | http://127.0.0.1:10111/sub?target=singbox&column=region 67 | http://127.0.0.1:10111/sub?target=clash&column=city 68 | 69 | http://127.0.0.1:10111/sub?target=v2ray&source=data\result.csv 70 | http://127.0.0.1:10111/sub?target=singbox&source=??? 71 | http://127.0.0.1:10111/sub?target=clash&source=??? 72 | ————————————————————————————————————————————————————————————————————————————————————————————————— 73 | 74 | 订阅URL的参数介绍: 75 | 76 | - target 77 | - v2ray:v2ray订阅,默认 78 | - singbox:sing-box订阅 79 | - clash:clash订阅 80 | - page:订阅分页,从1开始,默认是1 81 | - template 82 | - true:启用订阅模板,默认 83 | - false:不启用订阅模板 84 | - n/nodeSize 85 | - 自定义节点数量 86 | - dport/defaultPort 87 | - 默认端口,初始值为0,表示随机端口,有效值范围:[80,65535] 88 | - tls:剔除不要的端口和配置 89 | - true:加密TLS端口和配置 90 | - false:非加密TLS端口和配置 91 | - all:默认,不区分加密TLS和非加密TLS的端口和配置 92 | - type/proxyType 93 | - vless:vless协议,默认 94 | - trojan:trojan协议 95 | - ss:ss-v2ray协议 96 | - id/userid 97 | - 0,表示随机选择,有效值范围:[1,255] 98 | - column/columnName 只有csv中有对应的字段才生效,否则为空 99 | - colo,数据中心(3个字母),默认 100 | - loc,国家代码(2个字母) 101 | - region,地区 102 | - city,城市 103 | - source/dataSource 104 | - 指定数据源所在文件夹路径或文件路径/URL链接,默认是文件夹data的路径 105 | 106 | 特别说明: 107 | 108 | 1、n(nodesize、nodecount):您需要的节点数量。是从数据源所在路径中,读取的所有数据中 109 | 注意: 110 | (1)如果文件内的txt、csv文件比较多,读取第一个文件的个数小于nodesize,自然要读取下一个文件的数据, 111 | 就会出现,哪个文件的数据在前,哪个文件数据在后的问题,这个跟文件的默认排序有关; 112 | (2)不是从读取的全部数据中,随机nodesize个数据,而是按照读取到的数据先后顺序 113 | (3)默认值:v2ray默认是300个节点;sing-box、clash默认50个节点,最大150个节点。 114 | (4)可能因TLS模式、端口和workers.dev的节点冲突,多次都随机选择它们,出现冲突,不符合的要求,多次丢弃,导致生成的节点少(不是因IP地址数据少导致)。 115 | 2、type(proxytype):选择什么协议的节点?只能选择vless、trojan,这里指您在配置文件中,存放的节点类型,符合要求的,才使用它。 116 | 3、id(userid):指定使用配置文件的哪个节点,生成v2ray链接或sing-box、clash配置文件?它的值是虚构的,是根据配置文件的数组下标+1来计算的。 117 | 例如: 118 | id=1就是使用第一个节点的配置信息,2就是使用第二个节点的配置信息,以此类推。 119 | id值的范围是[0,255],为0是随机节点的配置信息,超过配置的总个数,也是随机节点的配置信息。 120 | 注意: 121 | type 和 id 两个都设置且设置不当,可能导致生成空白页面,要传入正确的值才能生成节点信息。 122 | 例如:type=vless&id=2,配置文件中第2个节点不是vless,就不能生成节点的配置信息,导致空白页面出现。 123 | 124 | 4、tls(tlsMode):用于控制使用哪些端口(包括使用哪些节点)。 125 | tls=true/1表示使用加密TLS端口和节点; 126 | false/0表示使用非加密TLS的端口和节点(生成的trojan可能有问题); 127 | 如果为空/不传入该参数,就不区分TLS和非TLS,端口同样不区别。 128 | 5、dport(defaultPort):默认0端口,随机TLS端口或随机非TLS端口。 129 | data目录下,读取到txt、csv文件的数据中,没有端口的情况,才使用这里设置的默认端口,host含有workers.dev,就由内部随机生成。 130 | 注意:这里设置的端口不保证应用到所有节点中,程序可能会根据是否含有workers.dev改为其它端口(随机TLS端口或非TLS端口)。 131 | 6、column(columnName): 132 | 选择csv文件中哪个列名作为节点的前缀,在csv中找不到对应的字段(映照关系看file_data.rs的create_field_map函数),就默认为空。 133 | 该值只能选[colo,loc,region,city]中任意一个,设置其它值都默认为colo。colo对应的所有可能列名都找不到就说明没有,就默认是空字符串。 134 | 7、source(dataSource):默认是文件夹data的路径,支持文件夹或文件的相对路径和绝对路径;支持csv、txt后缀的网络资源URL。 135 | 注意: 136 | 1、订阅使用前,在浏览器中,验证路径是否正确。 137 | 2、当传入的是文件路径,必须是txt或csv的有效文件路径。 138 | 3、当传入的是文件夹路径,该路径下,必须含有txt或csv文件。 139 | 4、当传入的是网络链接,必须以"https://"开头,".csv"或".txt"结尾的链接。 140 | 141 | ————————————————————————————————————————————————————————————————————————————————————————————————— 142 | 温馨提示: 143 | 144 | 1、使用 Cloudflare workers 搭建的 vless/trojan/ss-v2ray 节点,转换为 singbox/clash.mate/mihomo 订阅使用,PROXYIP 地址可能会丢失,跟没有设置 PROXYIP 效果一样,也就是不能使用它访问一些地区封锁的网站,比如:ChatGPT、Netflix 等。 145 | 2、ss-v2ray+tls+websocket,订阅链接(v2ray)只能在支持v2ray-plugin扩展的代理客户端中使用,直接复制或在v2rayN中订阅无法添加使用的,经测试可以黏贴到NekoBox中使用。 146 | 例如:ss://bm9uZTowMTk1YzQ3Ni02M2Y4LTcyMzItYWM0Mi0wYWMxZWFhYmU5MzQ=@104.19.177.137:2096?plugin=v2ray-plugin;tls;mux%3D0;mode%3Dwebsocket;path%3D/192.168.1.1-443;host%3Dss1.pages.dev#⑤|104.19.177.137:2096 --------------------------------------------------------------------------------