sublinker / node /clash.go
YimoEx
init
bb9df9e
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
}