1
0
mirror of https://github.com/nitezs/sub2sing-box.git synced 2024-12-23 21:24:42 -05:00
This commit is contained in:
Nite07 2024-03-11 21:00:13 +08:00
parent 665da8fc45
commit daa3ab6867
18 changed files with 80 additions and 100 deletions

View File

@ -13,5 +13,5 @@ Flags:
-o, --output string output file path -o, --output string output file path
-p, --proxy strings common proxies -p, --proxy strings common proxies
-s, --subscription strings subscription urls -s, --subscription strings subscription urls
-t, --template string path of template file -t, --template string template file path
``` ```

View File

@ -15,6 +15,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
//TODO: 过滤、去重、分组、排序
var convertCmd = &cobra.Command{ var convertCmd = &cobra.Command{
Use: "convert", Use: "convert",
Long: "Convert common proxy to sing-box proxy", Long: "Convert common proxy to sing-box proxy",
@ -24,63 +26,57 @@ var convertCmd = &cobra.Command{
proxies, _ := cmd.Flags().GetStringSlice("proxy") proxies, _ := cmd.Flags().GetStringSlice("proxy")
template, _ := cmd.Flags().GetString("template") template, _ := cmd.Flags().GetString("template")
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
if template == "" { result := ""
proxyList, err := ConvertSubscriptionsToSProxy(subscriptions) 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 { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
for _, p := range proxies { proxyList = append(proxyList, p)
result, err := ConvertCProxyToSProxy(p) }
if err != nil {
fmt.Println(err) if template != "" {
return result, err = MergeTemplate(proxyList, template)
}
proxyList = append(proxyList, result)
}
result, err := json.Marshal(proxyList)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
if output != "" {
err = os.WriteFile(output, result, 0666)
if err != nil {
fmt.Println(err)
return
}
} else {
fmt.Println(string(result))
}
} else { } else {
config, err := ConvertWithTemplate(subscriptions, proxies, template) r, err := json.Marshal(proxyList)
result = string(r)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return 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() { func init() {
convertCmd.Flags().StringSliceP("subscription", "s", []string{}, "subscription urls") convertCmd.Flags().StringSliceP("subscription", "s", []string{}, "subscription urls")
convertCmd.Flags().StringSliceP("proxy", "p", []string{}, "common proxies") 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("output", "o", "", "output file path")
convertCmd.Flags().StringP("filter", "f", "", "outbound tag filter (support regex)")
RootCmd.AddCommand(convertCmd) RootCmd.AddCommand(convertCmd)
} }
@ -101,44 +97,31 @@ func Convert(urls []string, proxies []string) ([]model.Proxy, error) {
return proxyList, nil return proxyList, nil
} }
func ConvertWithTemplate(urls []string, proxies []string, template string) (model.Config, error) { func MergeTemplate(proxies []model.Proxy, template string) (string, 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)
}
config, err := ReadTemplate(template) config, err := ReadTemplate(template)
proxyTags := make([]string, 0)
if err != nil { 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 { if err != nil {
return model.Config{}, err return "", err
} }
var newOutbounds []model.Outbound var newOutbounds []model.Outbound
err = json.Unmarshal(ps, &newOutbounds) err = json.Unmarshal(ps, &newOutbounds)
if err != nil { 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 { for i, outbound := range config.Outbounds {
if outbound.Type == "urltest" || outbound.Type == "selector" { if outbound.Type == "urltest" || outbound.Type == "selector" {
var parsedOutbound []string = make([]string, 0) var parsedOutbound []string = make([]string, 0)
for _, o := range outbound.Outbounds { for _, o := range outbound.Outbounds {
if o == "<all-proxy-tags>" { if o == "<all-proxy-tags>" {
parsedOutbound = append(parsedOutbound, newOutboundTagList...) parsedOutbound = append(parsedOutbound, proxyTags...)
} else { } else {
parsedOutbound = append(parsedOutbound, o) parsedOutbound = append(parsedOutbound, o)
} }
@ -146,7 +129,12 @@ func ConvertWithTemplate(urls []string, proxies []string, template string) (mode
config.Outbounds[i].Outbounds = parsedOutbound 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) { func ConvertCProxyToSProxy(proxy string) (model.Proxy, error) {
@ -180,13 +168,11 @@ func FetchSubscription(url string, maxRetryTime int) (string, error) {
for retryTime < maxRetryTime { for retryTime < maxRetryTime {
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
fmt.Println(err)
retryTime++ retryTime++
continue continue
} }
data, err := io.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
fmt.Println(err)
retryTime++ retryTime++
continue continue
} }
@ -200,12 +186,10 @@ func ConvertSubscriptionsToSProxy(urls []string) ([]model.Proxy, error) {
for _, url := range urls { for _, url := range urls {
data, err := FetchSubscription(url, 3) data, err := FetchSubscription(url, 3)
if err != nil { if err != nil {
fmt.Println(err)
return nil, err return nil, err
} }
proxy, err := DecodeBase64(data) proxy, err := DecodeBase64(data)
if err != nil { if err != nil {
fmt.Println(err)
return nil, err return nil, err
} }
proxies := strings.Split(proxy, "\n") proxies := strings.Split(proxy, "\n")

View File

@ -91,7 +91,7 @@ type DNSRule struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Inbound Listable[string] `json:"inbound,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,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"` Network Listable[string] `json:"network,omitempty"`
AuthUser Listable[string] `json:"auth_user,omitempty"` AuthUser Listable[string] `json:"auth_user,omitempty"`
Protocol Listable[string] `json:"protocol,omitempty"` Protocol Listable[string] `json:"protocol,omitempty"`
@ -126,7 +126,7 @@ type DNSRule struct {
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL uint32 `json:"rewrite_ttl,omitempty"` RewriteTTL uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet string `json:"client_subnet,omitempty"` ClientSubnet string `json:"client_subnet,omitempty"`
Mode string `json:"mode"` Mode string `json:"mode,omitempty"`
Rules Listable[DNSRule] `json:"rules,omitempty"` Rules Listable[DNSRule] `json:"rules,omitempty"`
} }

View File

@ -1,8 +1,6 @@
package model package model
type Hysteria struct { type Hysteria struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
Up string `json:"up,omitempty"` Up string `json:"up,omitempty"`

View File

@ -6,8 +6,6 @@ type Hysteria2Obfs struct {
} }
type Hysteria2 struct { type Hysteria2 struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
UpMbps int `json:"up_mbps,omitempty"` UpMbps int `json:"up_mbps,omitempty"`

View File

@ -6,6 +6,7 @@ import (
type Proxy struct { type Proxy struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Shadowsocks `json:"-"` Shadowsocks `json:"-"`
VMess `json:"-"` VMess `json:"-"`
VLESS `json:"-"` VLESS `json:"-"`
@ -20,57 +21,71 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
case "shadowsocks": case "shadowsocks":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Shadowsocks Shadowsocks
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
Shadowsocks: p.Shadowsocks, Shadowsocks: p.Shadowsocks,
}) })
case "vmess": case "vmess":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
VMess VMess
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
VMess: p.VMess, VMess: p.VMess,
}) })
case "vless": case "vless":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
VLESS VLESS
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
VLESS: p.VLESS, VLESS: p.VLESS,
}) })
case "trojan": case "trojan":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Trojan Trojan
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
Trojan: p.Trojan, Trojan: p.Trojan,
}) })
case "tuic": case "tuic":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
TUIC TUIC
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
TUIC: p.TUIC, TUIC: p.TUIC,
}) })
case "hysteria": case "hysteria":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Hysteria Hysteria
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
Hysteria: p.Hysteria, Hysteria: p.Hysteria,
}) })
case "hysteria2": case "hysteria2":
return json.Marshal(&struct { return json.Marshal(&struct {
Type string `json:"type"` Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Hysteria2 Hysteria2
}{ }{
Type: p.Type, Type: p.Type,
Tag: p.Tag,
Hysteria2: p.Hysteria2, Hysteria2: p.Hysteria2,
}) })
default: default:

View File

@ -1,7 +1,6 @@
package model package model
type Shadowsocks struct { type Shadowsocks struct {
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
Method string `json:"method"` Method string `json:"method"`

View File

@ -1,8 +1,6 @@
package model package model
type Trojan struct { type Trojan struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
Password string `json:"password"` Password string `json:"password"`

View File

@ -1,8 +1,6 @@
package model package model
type TUIC struct { type TUIC struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
UUID string `json:"uuid,omitempty"` UUID string `json:"uuid,omitempty"`

View File

@ -1,8 +1,6 @@
package model package model
type VLESS struct { type VLESS struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
UUID string `json:"uuid"` UUID string `json:"uuid"`

View File

@ -19,8 +19,6 @@ type VmessJson struct {
} }
type VMess struct { type VMess struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Server string `json:"server"` Server string `json:"server"`
ServerPort uint16 `json:"server_port"` ServerPort uint16 `json:"server_port"`
UUID string `json:"uuid"` UUID string `json:"uuid"`

View File

@ -63,8 +63,8 @@ func ParseHysteria(proxy string) (model.Proxy, error) {
} }
result := model.Proxy{ result := model.Proxy{
Type: "hysteria", Type: "hysteria",
Tag: remarks,
Hysteria: model.Hysteria{ Hysteria: model.Hysteria{
Tag: remarks,
Server: host, Server: host,
ServerPort: uint16(port), ServerPort: uint16(port),
Up: upmbps, Up: upmbps,

View File

@ -36,8 +36,8 @@ func ParseHysteria2(proxy string) (model.Proxy, error) {
network := params.Get("network") network := params.Get("network")
result := model.Proxy{ result := model.Proxy{
Type: "hysteria2", Type: "hysteria2",
Tag: remarks,
Hysteria2: model.Hysteria2{ Hysteria2: model.Hysteria2{
Tag: remarks,
Server: server, Server: server,
ServerPort: uint16(port), ServerPort: uint16(port),
Password: password, Password: password,

View File

@ -50,14 +50,10 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
method := credentials[0] method := credentials[0]
password := credentials[1] password := credentials[1]
server := strings.TrimSpace(serverAndPort[0]) server := strings.TrimSpace(serverAndPort[0])
// params, err := url.ParseQuery(proxy)
// if err != nil {
// return model.Proxy{}, err
// }
result := model.Proxy{ result := model.Proxy{
Type: "shadowsocks", Type: "shadowsocks",
Tag: remarks,
Shadowsocks: model.Shadowsocks{ Shadowsocks: model.Shadowsocks{
Tag: remarks,
Method: method, Method: method,
Password: password, Password: password,
Server: server, Server: server,

View File

@ -40,8 +40,8 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
password := strings.TrimSpace(parts[0]) password := strings.TrimSpace(parts[0])
result := model.Proxy{ result := model.Proxy{
Type: "trojan", Type: "trojan",
Tag: remarks,
Trojan: model.Trojan{ Trojan: model.Trojan{
Tag: remarks,
Server: server, Server: server,
ServerPort: uint16(port), ServerPort: uint16(port),
Password: password, Password: password,

View File

@ -48,15 +48,13 @@ func ParseVless(proxy string) (model.Proxy, error) {
} }
server := strings.TrimSpace(serverAndPort[0]) server := strings.TrimSpace(serverAndPort[0])
uuid := strings.TrimSpace(parts[0]) uuid := strings.TrimSpace(parts[0])
network := params.Get("type")
result := model.Proxy{ result := model.Proxy{
Type: "vless", Type: "vless",
Tag: remarks,
VLESS: model.VLESS{ VLESS: model.VLESS{
Tag: remarks,
Server: server, Server: server,
ServerPort: uint16(port), ServerPort: uint16(port),
UUID: uuid, UUID: uuid,
Network: network,
Flow: params.Get("flow"), Flow: params.Get("flow"),
}, },
} }

View File

@ -54,8 +54,8 @@ func ParseVmess(proxy string) (model.Proxy, error) {
result := model.Proxy{ result := model.Proxy{
Type: "vmess", Type: "vmess",
Tag: name,
VMess: model.VMess{ VMess: model.VMess{
Tag: name,
Server: vmess.Add, Server: vmess.Add,
ServerPort: uint16(port), ServerPort: uint16(port),
UUID: vmess.Id, UUID: vmess.Id,

View File

@ -161,7 +161,7 @@
"outbounds": [ "outbounds": [
{ {
"type": "selector", "type": "selector",
"tag": "手动切换", "tag": "节点选择",
"outbounds": ["<all-proxy-tags>", "direct"], "outbounds": ["<all-proxy-tags>", "direct"],
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
@ -175,35 +175,35 @@
{ {
"type": "selector", "type": "selector",
"tag": "Microsoft", "tag": "Microsoft",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"], "outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择", "default": "节点选择",
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
{ {
"type": "selector", "type": "selector",
"tag": "Bilibili", "tag": "Bilibili",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"], "outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择", "default": "节点选择",
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
{ {
"type": "selector", "type": "selector",
"tag": "Games(全球)", "tag": "Games(全球)",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"], "outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择", "default": "节点选择",
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
{ {
"type": "selector", "type": "selector",
"tag": "Games(中国)", "tag": "Games(中国)",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"], "outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择", "default": "节点选择",
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
{ {
"type": "selector", "type": "selector",
"tag": "Bahamut", "tag": "Bahamut",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"], "outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择", "default": "节点选择",
"interrupt_exist_connections": true "interrupt_exist_connections": true
}, },
@ -228,7 +228,7 @@
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "./ui", "external_ui": "./ui",
"external_ui_download_detour": "手动切换" "external_ui_download_detour": "节点选择"
} }
} }
} }