Spaces:
Sleeping
Sleeping
| package node | |
| import ( | |
| "fmt" | |
| "io" | |
| "log" | |
| "net/http" | |
| "os" | |
| "strconv" | |
| "strings" | |
| "gopkg.in/yaml.v3" | |
| ) | |
| type Proxy struct { | |
| Name string `yaml:"name,omitempty"` | |
| Type string `yaml:"type,omitempty"` | |
| Server string `yaml:"server,omitempty"` | |
| Port int `yaml:"port,omitempty"` | |
| Cipher string `yaml:"cipher,omitempty"` | |
| Password string `yaml:"password,omitempty"` | |
| Client_fingerprint string `yaml:"client-fingerprint,omitempty"` | |
| Tfo bool `yaml:"tfo,omitempty"` | |
| Udp bool `yaml:"udp,omitempty"` | |
| Skip_cert_verify bool `yaml:"skip-cert-verify,omitempty"` | |
| Tls bool `yaml:"tls,omitempty"` | |
| Servername string `yaml:"servername,omitempty"` | |
| Flow string `yaml:"flow,omitempty"` | |
| AlterId string `yaml:"alterId,omitempty"` | |
| Network string `yaml:"network,omitempty"` | |
| Reality_opts map[string]interface{} `yaml:"reality-opts,omitempty"` | |
| Ws_opts map[string]interface{} `yaml:"ws-opts,omitempty"` | |
| Auth_str string `yaml:"auth_str,omitempty"` | |
| Auth string `yaml:"auth,omitempty"` | |
| Up int `yaml:"up,omitempty"` | |
| Down int `yaml:"down,omitempty"` | |
| Alpn []string `yaml:"alpn,omitempty"` | |
| Sni string `yaml:"sni,omitempty"` | |
| Obfs string `yaml:"obfs,omitempty"` | |
| Obfs_password string `yaml:"obfs-password,omitempty"` | |
| Protocol string `yaml:"protocol,omitempty"` | |
| Uuid string `yaml:"uuid,omitempty"` | |
| Peer string `yaml:"peer,omitempty"` | |
| Congestion_control string `yaml:"congestion_control,omitempty"` | |
| Udp_relay_mode string `yaml:"udp_relay_mode,omitempty"` | |
| Disable_sni bool `yaml:"disable_sni,omitempty"` | |
| } | |
| type ProxyGroup struct { | |
| Proxies []string `yaml:"proxies"` | |
| } | |
| type Config struct { | |
| Proxies []Proxy `yaml:"proxies"` | |
| Proxy_groups []ProxyGroup `yaml:"proxy-groups"` | |
| } | |
| // 删除opts中的空值 | |
| func DeleteOpts(opts map[string]interface{}) { | |
| for k, v := range opts { | |
| switch v := v.(type) { | |
| case string: | |
| if v == "" { | |
| delete(opts, k) | |
| } | |
| case map[string]interface{}: | |
| DeleteOpts(v) | |
| if len(v) == 0 { | |
| delete(opts, k) | |
| } | |
| } | |
| } | |
| } | |
| func convertToInt(value interface{}) (int, error) { | |
| switch v := value.(type) { | |
| case int: | |
| return v, nil | |
| case float64: | |
| return int(v), nil | |
| case string: | |
| return strconv.Atoi(v) | |
| default: | |
| return 0, fmt.Errorf("unexpected type %T", v) | |
| } | |
| } | |
| // EncodeClash 用于生成 Clash 配置文件 | |
| func EncodeClash(urls []string, sqlconfig SqlConfig) ([]byte, error) { | |
| // 传入urls,解析urls,生成proxys | |
| // yamlfile 为模板文件 | |
| var proxys []Proxy | |
| for _, link := range urls { | |
| Scheme := strings.Split(link, "://")[0] | |
| switch { | |
| case Scheme == "ss": | |
| ss, err := DecodeSSURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if ss.Name == "" { | |
| ss.Name = fmt.Sprintf("%s:%d", ss.Server, ss.Port) | |
| } | |
| ssproxy := Proxy{ | |
| Name: ss.Name, | |
| Type: "ss", | |
| Server: ss.Server, | |
| Port: ss.Port, | |
| Cipher: ss.Param.Cipher, | |
| Password: ss.Param.Password, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, ssproxy) | |
| case Scheme == "ssr": | |
| ssr, err := DecodeSSRURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if ssr.Qurey.Remarks == "" { | |
| ssr.Qurey.Remarks = fmt.Sprintf("%s:%d", ssr.Server, ssr.Port) | |
| } | |
| ssrproxy := Proxy{ | |
| Name: ssr.Qurey.Remarks, | |
| Type: "ssr", | |
| Server: ssr.Server, | |
| Port: ssr.Port, | |
| Cipher: ssr.Method, | |
| Password: ssr.Password, | |
| Obfs: ssr.Obfs, | |
| Obfs_password: ssr.Qurey.Obfsparam, | |
| Protocol: ssr.Protocol, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, ssrproxy) | |
| case Scheme == "trojan": | |
| trojan, err := DecodeTrojanURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if trojan.Name == "" { | |
| trojan.Name = fmt.Sprintf("%s:%d", trojan.Hostname, trojan.Port) | |
| } | |
| ws_opts := map[string]interface{}{ | |
| "path": trojan.Query.Path, | |
| "headers": map[string]interface{}{ | |
| "Host": trojan.Query.Host, | |
| }, | |
| } | |
| DeleteOpts(ws_opts) | |
| trojanproxy := Proxy{ | |
| Name: trojan.Name, | |
| Type: "trojan", | |
| Server: trojan.Hostname, | |
| Port: trojan.Port, | |
| Password: trojan.Password, | |
| Client_fingerprint: trojan.Query.Fp, | |
| Sni: trojan.Query.Sni, | |
| Network: trojan.Query.Type, | |
| Flow: trojan.Query.Flow, | |
| Alpn: trojan.Query.Alpn, | |
| Ws_opts: ws_opts, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, trojanproxy) | |
| case Scheme == "vmess": | |
| vmess, err := DecodeVMESSURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if vmess.Ps == "" { | |
| vmess.Ps = fmt.Sprintf("%s:%s", vmess.Add, vmess.Port) | |
| } | |
| ws_opts := map[string]interface{}{ | |
| "path": vmess.Path, | |
| "headers": map[string]interface{}{ | |
| "Host": vmess.Host, | |
| }, | |
| } | |
| DeleteOpts(ws_opts) | |
| tls := false | |
| if vmess.Tls != "none" && vmess.Tls != "" { | |
| tls = true | |
| } | |
| port, _ := convertToInt(vmess.Port) | |
| aid, _ := convertToInt(vmess.Aid) | |
| vmessproxy := Proxy{ | |
| Name: vmess.Ps, | |
| Type: "vmess", | |
| Server: vmess.Add, | |
| Port: port, | |
| Cipher: vmess.Scy, | |
| Uuid: vmess.Id, | |
| AlterId: strconv.Itoa(aid), | |
| Network: vmess.Net, | |
| Tls: tls, | |
| Ws_opts: ws_opts, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, vmessproxy) | |
| case Scheme == "vless": | |
| vless, err := DecodeVLESSURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if vless.Name == "" { | |
| vless.Name = fmt.Sprintf("%s:%d", vless.Server, vless.Port) | |
| } | |
| ws_opts := map[string]interface{}{ | |
| "path": vless.Query.Path, | |
| "headers": map[string]interface{}{ | |
| "Host": vless.Query.Host, | |
| }, | |
| } | |
| reality_opts := map[string]interface{}{ | |
| "public-key": vless.Query.Pbk, | |
| "short-id": vless.Query.Sid, | |
| } | |
| DeleteOpts(ws_opts) | |
| DeleteOpts(reality_opts) | |
| tls := false | |
| if vless.Query.Security != "" { | |
| tls = true | |
| } | |
| if vless.Query.Security == "none" { | |
| tls = false | |
| } | |
| vlessproxy := Proxy{ | |
| Name: vless.Name, | |
| Type: "vless", | |
| Server: vless.Server, | |
| Port: vless.Port, | |
| Servername: vless.Query.Sni, | |
| Uuid: vless.Uuid, | |
| Client_fingerprint: vless.Query.Fp, | |
| Network: vless.Query.Type, | |
| Flow: vless.Query.Flow, | |
| Alpn: vless.Query.Alpn, | |
| Ws_opts: ws_opts, | |
| Reality_opts: reality_opts, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| Tls: tls, | |
| } | |
| proxys = append(proxys, vlessproxy) | |
| case Scheme == "hy" || Scheme == "hysteria": | |
| hy, err := DecodeHYURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if hy.Name == "" { | |
| hy.Name = fmt.Sprintf("%s:%d", hy.Host, hy.Port) | |
| } | |
| hyproxy := Proxy{ | |
| Name: hy.Name, | |
| Type: "hysteria", | |
| Server: hy.Host, | |
| Port: hy.Port, | |
| Auth_str: hy.Auth, | |
| Up: hy.UpMbps, | |
| Down: hy.DownMbps, | |
| Alpn: hy.ALPN, | |
| Peer: hy.Peer, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, hyproxy) | |
| case Scheme == "hy2" || Scheme == "hysteria2": | |
| hy2, err := DecodeHY2URL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if hy2.Name == "" { | |
| hy2.Name = fmt.Sprintf("%s:%d", hy2.Host, hy2.Port) | |
| } | |
| hyproxy2 := Proxy{ | |
| Name: hy2.Name, | |
| Type: "hysteria2", | |
| Server: hy2.Host, | |
| Port: hy2.Port, | |
| Auth_str: hy2.Auth, | |
| Sni: hy2.Sni, | |
| Alpn: hy2.ALPN, | |
| Obfs: hy2.Obfs, | |
| Password: hy2.Password, | |
| Obfs_password: hy2.ObfsPassword, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, hyproxy2) | |
| case Scheme == "tuic": | |
| tuic, err := DecodeTuicURL(link) | |
| if err != nil { | |
| log.Println(err) | |
| continue | |
| } | |
| // 如果没有名字,就用服务器地址作为名字 | |
| if tuic.Name == "" { | |
| tuic.Name = fmt.Sprintf("%s:%d", tuic.Host, tuic.Port) | |
| } | |
| disable_sni := false | |
| if tuic.Disable_sni == 1 { | |
| disable_sni = true | |
| } | |
| tuicproxy := Proxy{ | |
| Name: tuic.Name, | |
| Type: "tuic", | |
| Server: tuic.Host, | |
| Port: tuic.Port, | |
| Password: tuic.Password, | |
| Uuid: tuic.Uuid, | |
| Congestion_control: tuic.Congestion_control, | |
| Alpn: tuic.Alpn, | |
| Udp_relay_mode: tuic.Udp_relay_mode, | |
| Disable_sni: disable_sni, | |
| Sni: tuic.Sni, | |
| Udp: sqlconfig.Udp, | |
| Skip_cert_verify: sqlconfig.Cert, | |
| } | |
| proxys = append(proxys, tuicproxy) | |
| } | |
| } | |
| // 生成Clash配置文件 | |
| return DecodeClash(proxys, sqlconfig.Clash) | |
| } | |
| // DecodeClash 用于解析 Clash 配置文件 | |
| func DecodeClash(proxys []Proxy, yamlfile string) ([]byte, error) { | |
| // 读取 YAML 文件 | |
| var data []byte | |
| var err error | |
| if strings.Contains(yamlfile, "://") { | |
| resp, err := http.Get(yamlfile) | |
| if err != nil { | |
| log.Println("http.Get error", err) | |
| return nil, err | |
| } | |
| defer resp.Body.Close() | |
| data, err = io.ReadAll(resp.Body) | |
| if err != nil { | |
| log.Printf("error: %v", err) | |
| return nil, err | |
| } | |
| } else { | |
| data, err = os.ReadFile(yamlfile) | |
| if err != nil { | |
| log.Printf("error: %v", err) | |
| return nil, err | |
| } | |
| } | |
| // 解析 YAML 文件 | |
| config := make(map[interface{}]interface{}) | |
| err = yaml.Unmarshal(data, &config) | |
| if err != nil { | |
| log.Printf("error: %v", err) | |
| return nil, err | |
| } | |
| // 检查 "proxies" 键是否存在于 config 中 | |
| proxies, ok := config["proxies"].([]interface{}) | |
| if !ok { | |
| // 如果 "proxies" 键不存在,创建一个新的切片 | |
| proxies = []interface{}{} | |
| } | |
| // 定义一个代理列表名字 | |
| ProxiesNameList := []string{} | |
| // 添加新代理 | |
| for _, p := range proxys { | |
| ProxiesNameList = append(ProxiesNameList, p.Name) | |
| proxies = append(proxies, p) | |
| } | |
| // proxies = append(proxies, newProxy) | |
| config["proxies"] = proxies | |
| // 往ProxyGroup中插入代理列表 | |
| // ProxiesNameList := []string{"newProxy", "ceshi"} | |
| proxyGroups := config["proxy-groups"].([]interface{}) | |
| for i, pg := range proxyGroups { | |
| proxyGroup, ok := pg.(map[string]interface{}) | |
| if !ok { | |
| continue | |
| } | |
| // 如果 proxyGroup["proxies"] 是 nil,初始化它为一个空的切片 | |
| if proxyGroup["proxies"] == nil { | |
| proxyGroup["proxies"] = []interface{}{} | |
| } | |
| // 如果为链式代理的话则不插入返回 | |
| // log.Print("代理类型为:", proxyGroup["type"]) | |
| if proxyGroup["type"] == "relay" { | |
| break | |
| } | |
| // 清除 nil 值 | |
| var validProxies []interface{} | |
| for _, p := range proxyGroup["proxies"].([]interface{}) { | |
| if p != nil { | |
| validProxies = append(validProxies, p) | |
| } | |
| } | |
| // 添加新代理 | |
| for _, newProxy := range ProxiesNameList { | |
| validProxies = append(validProxies, newProxy) | |
| } | |
| proxyGroup["proxies"] = validProxies | |
| proxyGroups[i] = proxyGroup | |
| } | |
| config["proxy-groups"] = proxyGroups | |
| // 将修改后的内容写回文件 | |
| newData, err := yaml.Marshal(config) | |
| if err != nil { | |
| log.Printf("error: %v", err) | |
| } | |
| return newData, nil | |
| } | |