diff --git a/Readme.md b/Readme.md index 8d7a971..357d139 100644 --- a/Readme.md +++ b/Readme.md @@ -13,5 +13,5 @@ Flags: -o, --output string output file path -p, --proxy strings common proxies -s, --subscription strings subscription urls - -t, --template string path of template file + -t, --template string template file path ``` \ No newline at end of file diff --git a/cmd/convert.go b/cmd/convert.go index 64a77de..bd531ed 100644 --- a/cmd/convert.go +++ b/cmd/convert.go @@ -15,6 +15,8 @@ import ( "github.com/spf13/cobra" ) +//TODO: 过滤、去重、分组、排序 + var convertCmd = &cobra.Command{ Use: "convert", Long: "Convert common proxy to sing-box proxy", @@ -24,63 +26,57 @@ var convertCmd = &cobra.Command{ proxies, _ := cmd.Flags().GetStringSlice("proxy") template, _ := cmd.Flags().GetString("template") output, _ := cmd.Flags().GetString("output") - if template == "" { - proxyList, err := ConvertSubscriptionsToSProxy(subscriptions) + result := "" + var err error + + proxyList, err := ConvertSubscriptionsToSProxy(subscriptions) + if err != nil { + fmt.Println(err) + return + } + for _, proxy := range proxies { + p, err := ConvertCProxyToSProxy(proxy) if err != nil { fmt.Println(err) return } - for _, p := range proxies { - result, err := ConvertCProxyToSProxy(p) - if err != nil { - fmt.Println(err) - return - } - proxyList = append(proxyList, result) - } - result, err := json.Marshal(proxyList) + proxyList = append(proxyList, p) + } + + if template != "" { + result, err = MergeTemplate(proxyList, template) if err != nil { fmt.Println(err) return } - if output != "" { - err = os.WriteFile(output, result, 0666) - if err != nil { - fmt.Println(err) - return - } - } else { - fmt.Println(string(result)) - } } else { - config, err := ConvertWithTemplate(subscriptions, proxies, template) + r, err := json.Marshal(proxyList) + result = string(r) if err != nil { fmt.Println(err) return } - data, err := json.Marshal(config) - if err != nil { - fmt.Println(err) - return - } - if output != "" { - err = os.WriteFile(output, data, 0666) - if err != nil { - fmt.Println(err) - return - } - } else { - fmt.Println(string(data)) - } } + + if output != "" { + err = os.WriteFile(output, []byte(result), 0666) + if err != nil { + fmt.Println(err) + return + } + } else { + fmt.Println(string(result)) + } + }, } func init() { convertCmd.Flags().StringSliceP("subscription", "s", []string{}, "subscription urls") convertCmd.Flags().StringSliceP("proxy", "p", []string{}, "common proxies") - convertCmd.Flags().StringP("template", "t", "", "path of template file") + convertCmd.Flags().StringP("template", "t", "", "template file path") convertCmd.Flags().StringP("output", "o", "", "output file path") + convertCmd.Flags().StringP("filter", "f", "", "outbound tag filter (support regex)") RootCmd.AddCommand(convertCmd) } @@ -101,44 +97,31 @@ func Convert(urls []string, proxies []string) ([]model.Proxy, error) { return proxyList, nil } -func ConvertWithTemplate(urls []string, proxies []string, template string) (model.Config, error) { - proxyList := make([]model.Proxy, 0) - newProxies, err := ConvertSubscriptionsToSProxy(urls) - newOutboundTagList := make([]string, 0) - if err != nil { - return model.Config{}, err - } - proxyList = append(proxyList, newProxies...) - for _, p := range proxies { - proxy, err := ConvertCProxyToSProxy(p) - if err != nil { - return model.Config{}, err - } - proxyList = append(proxyList, proxy) - } +func MergeTemplate(proxies []model.Proxy, template string) (string, error) { config, err := ReadTemplate(template) + proxyTags := make([]string, 0) if err != nil { - return model.Config{}, err + return "", err } - ps, err := json.Marshal(proxyList) + for _, p := range proxies { + proxyTags = append(proxyTags, p.Tag) + } + ps, err := json.Marshal(&proxies) + fmt.Print(string(ps)) if err != nil { - return model.Config{}, err + return "", err } var newOutbounds []model.Outbound err = json.Unmarshal(ps, &newOutbounds) if err != nil { - return model.Config{}, err + return "", err } - for _, outbound := range newOutbounds { - newOutboundTagList = append(newOutboundTagList, outbound.Tag) - } - config.Outbounds = append(config.Outbounds, newOutbounds...) for i, outbound := range config.Outbounds { if outbound.Type == "urltest" || outbound.Type == "selector" { var parsedOutbound []string = make([]string, 0) for _, o := range outbound.Outbounds { if o == "" { - parsedOutbound = append(parsedOutbound, newOutboundTagList...) + parsedOutbound = append(parsedOutbound, proxyTags...) } else { parsedOutbound = append(parsedOutbound, o) } @@ -146,7 +129,12 @@ func ConvertWithTemplate(urls []string, proxies []string, template string) (mode config.Outbounds[i].Outbounds = parsedOutbound } } - return config, nil + config.Outbounds = append(config.Outbounds, newOutbounds...) + data, err := json.Marshal(config) + if err != nil { + return "", err + } + return string(data), nil } func ConvertCProxyToSProxy(proxy string) (model.Proxy, error) { @@ -180,13 +168,11 @@ func FetchSubscription(url string, maxRetryTime int) (string, error) { for retryTime < maxRetryTime { resp, err := http.Get(url) if err != nil { - fmt.Println(err) retryTime++ continue } data, err := io.ReadAll(resp.Body) if err != nil { - fmt.Println(err) retryTime++ continue } @@ -200,12 +186,10 @@ func ConvertSubscriptionsToSProxy(urls []string) ([]model.Proxy, error) { for _, url := range urls { data, err := FetchSubscription(url, 3) if err != nil { - fmt.Println(err) return nil, err } proxy, err := DecodeBase64(data) if err != nil { - fmt.Println(err) return nil, err } proxies := strings.Split(proxy, "\n") diff --git a/model/config.go b/model/config.go index 48b405a..fdf0e2c 100644 --- a/model/config.go +++ b/model/config.go @@ -91,7 +91,7 @@ type DNSRule struct { Type string `json:"type,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"` IPVersion int `json:"ip_version,omitempty"` - QueryType Listable[uint16] `json:"query_type,omitempty"` + QueryType Listable[string] `json:"query_type,omitempty"` Network Listable[string] `json:"network,omitempty"` AuthUser Listable[string] `json:"auth_user,omitempty"` Protocol Listable[string] `json:"protocol,omitempty"` @@ -126,7 +126,7 @@ type DNSRule struct { DisableCache bool `json:"disable_cache,omitempty"` RewriteTTL uint32 `json:"rewrite_ttl,omitempty"` ClientSubnet string `json:"client_subnet,omitempty"` - Mode string `json:"mode"` + Mode string `json:"mode,omitempty"` Rules Listable[DNSRule] `json:"rules,omitempty"` } diff --git a/model/hysteria.go b/model/hysteria.go index 3e93205..bf299af 100644 --- a/model/hysteria.go +++ b/model/hysteria.go @@ -1,8 +1,6 @@ package model type Hysteria struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` Up string `json:"up,omitempty"` diff --git a/model/hysteria2.go b/model/hysteria2.go index 32263f7..066a341 100644 --- a/model/hysteria2.go +++ b/model/hysteria2.go @@ -6,8 +6,6 @@ type Hysteria2Obfs struct { } type Hysteria2 struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` UpMbps int `json:"up_mbps,omitempty"` diff --git a/model/proxy.go b/model/proxy.go index 2418131..2c76e55 100644 --- a/model/proxy.go +++ b/model/proxy.go @@ -6,6 +6,7 @@ import ( type Proxy struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` Shadowsocks `json:"-"` VMess `json:"-"` VLESS `json:"-"` @@ -20,57 +21,71 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { case "shadowsocks": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` Shadowsocks }{ Type: p.Type, + Tag: p.Tag, Shadowsocks: p.Shadowsocks, }) case "vmess": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` VMess }{ Type: p.Type, + Tag: p.Tag, VMess: p.VMess, }) case "vless": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` VLESS }{ Type: p.Type, + Tag: p.Tag, VLESS: p.VLESS, }) case "trojan": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` Trojan }{ Type: p.Type, + Tag: p.Tag, Trojan: p.Trojan, }) case "tuic": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` TUIC }{ Type: p.Type, + Tag: p.Tag, TUIC: p.TUIC, }) case "hysteria": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` Hysteria }{ Type: p.Type, + Tag: p.Tag, Hysteria: p.Hysteria, }) case "hysteria2": return json.Marshal(&struct { Type string `json:"type"` + Tag string `json:"tag,omitempty"` Hysteria2 }{ Type: p.Type, + Tag: p.Tag, Hysteria2: p.Hysteria2, }) default: diff --git a/model/shadowsocks.go b/model/shadowsocks.go index bc9e0af..5cdc8b1 100644 --- a/model/shadowsocks.go +++ b/model/shadowsocks.go @@ -1,7 +1,6 @@ package model type Shadowsocks struct { - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` Method string `json:"method"` diff --git a/model/trojan.go b/model/trojan.go index 5571aba..5e827fa 100644 --- a/model/trojan.go +++ b/model/trojan.go @@ -1,8 +1,6 @@ package model type Trojan struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` Password string `json:"password"` diff --git a/model/tuic.go b/model/tuic.go index c17854e..304c57d 100644 --- a/model/tuic.go +++ b/model/tuic.go @@ -1,8 +1,6 @@ package model type TUIC struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` UUID string `json:"uuid,omitempty"` diff --git a/model/vless.go b/model/vless.go index 95b825a..74e355e 100644 --- a/model/vless.go +++ b/model/vless.go @@ -1,8 +1,6 @@ package model type VLESS struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` UUID string `json:"uuid"` diff --git a/model/vmess.go b/model/vmess.go index a8f0b1e..472bbfb 100644 --- a/model/vmess.go +++ b/model/vmess.go @@ -19,8 +19,6 @@ type VmessJson struct { } type VMess struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` Server string `json:"server"` ServerPort uint16 `json:"server_port"` UUID string `json:"uuid"` diff --git a/parser/hysteria.go b/parser/hysteria.go index d270bcf..5b3be9a 100644 --- a/parser/hysteria.go +++ b/parser/hysteria.go @@ -63,8 +63,8 @@ func ParseHysteria(proxy string) (model.Proxy, error) { } result := model.Proxy{ Type: "hysteria", + Tag: remarks, Hysteria: model.Hysteria{ - Tag: remarks, Server: host, ServerPort: uint16(port), Up: upmbps, diff --git a/parser/hysteria2.go b/parser/hysteria2.go index c8f903f..ab09dee 100644 --- a/parser/hysteria2.go +++ b/parser/hysteria2.go @@ -36,8 +36,8 @@ func ParseHysteria2(proxy string) (model.Proxy, error) { network := params.Get("network") result := model.Proxy{ Type: "hysteria2", + Tag: remarks, Hysteria2: model.Hysteria2{ - Tag: remarks, Server: server, ServerPort: uint16(port), Password: password, diff --git a/parser/shadowsocks.go b/parser/shadowsocks.go index c9f8ced..7417277 100644 --- a/parser/shadowsocks.go +++ b/parser/shadowsocks.go @@ -50,14 +50,10 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) { method := credentials[0] password := credentials[1] server := strings.TrimSpace(serverAndPort[0]) - // params, err := url.ParseQuery(proxy) - // if err != nil { - // return model.Proxy{}, err - // } result := model.Proxy{ Type: "shadowsocks", + Tag: remarks, Shadowsocks: model.Shadowsocks{ - Tag: remarks, Method: method, Password: password, Server: server, diff --git a/parser/trojan.go b/parser/trojan.go index 025c273..f2253e3 100644 --- a/parser/trojan.go +++ b/parser/trojan.go @@ -40,8 +40,8 @@ func ParseTrojan(proxy string) (model.Proxy, error) { password := strings.TrimSpace(parts[0]) result := model.Proxy{ Type: "trojan", + Tag: remarks, Trojan: model.Trojan{ - Tag: remarks, Server: server, ServerPort: uint16(port), Password: password, diff --git a/parser/vless.go b/parser/vless.go index a8cab0d..f9beccc 100644 --- a/parser/vless.go +++ b/parser/vless.go @@ -48,15 +48,13 @@ func ParseVless(proxy string) (model.Proxy, error) { } server := strings.TrimSpace(serverAndPort[0]) uuid := strings.TrimSpace(parts[0]) - network := params.Get("type") result := model.Proxy{ Type: "vless", + Tag: remarks, VLESS: model.VLESS{ - Tag: remarks, Server: server, ServerPort: uint16(port), UUID: uuid, - Network: network, Flow: params.Get("flow"), }, } diff --git a/parser/vmess.go b/parser/vmess.go index d5ec23f..f757453 100644 --- a/parser/vmess.go +++ b/parser/vmess.go @@ -54,8 +54,8 @@ func ParseVmess(proxy string) (model.Proxy, error) { result := model.Proxy{ Type: "vmess", + Tag: name, VMess: model.VMess{ - Tag: name, Server: vmess.Add, ServerPort: uint16(port), UUID: vmess.Id, diff --git a/template/tun-fakeip.json b/template/tun-fakeip.json index d38dcc1..8bca5f3 100644 --- a/template/tun-fakeip.json +++ b/template/tun-fakeip.json @@ -161,7 +161,7 @@ "outbounds": [ { "type": "selector", - "tag": "手动切换", + "tag": "节点选择", "outbounds": ["", "direct"], "interrupt_exist_connections": true }, @@ -175,35 +175,35 @@ { "type": "selector", "tag": "Microsoft", - "outbounds": ["节点选择", "手动切换", "", "direct"], + "outbounds": ["节点选择", "", "direct"], "default": "节点选择", "interrupt_exist_connections": true }, { "type": "selector", "tag": "Bilibili", - "outbounds": ["节点选择", "手动切换", "", "direct"], + "outbounds": ["节点选择", "", "direct"], "default": "节点选择", "interrupt_exist_connections": true }, { "type": "selector", "tag": "Games(全球)", - "outbounds": ["节点选择", "手动切换", "", "direct"], + "outbounds": ["节点选择", "", "direct"], "default": "节点选择", "interrupt_exist_connections": true }, { "type": "selector", "tag": "Games(中国)", - "outbounds": ["节点选择", "手动切换", "", "direct"], + "outbounds": ["节点选择", "", "direct"], "default": "节点选择", "interrupt_exist_connections": true }, { "type": "selector", "tag": "Bahamut", - "outbounds": ["节点选择", "手动切换", "", "direct"], + "outbounds": ["节点选择", "", "direct"], "default": "节点选择", "interrupt_exist_connections": true }, @@ -228,7 +228,7 @@ "clash_api": { "external_controller": "127.0.0.1:9090", "external_ui": "./ui", - "external_ui_download_detour": "手动切换" + "external_ui_download_detour": "节点选择" } } }