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
-p, --proxy strings common proxies
-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"
)
//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 == "<all-proxy-tags>" {
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")

View File

@ -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"`
}

View File

@ -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"`

View File

@ -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"`

View File

@ -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:

View File

@ -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"`

View File

@ -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"`

View File

@ -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"`

View File

@ -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"`

View File

@ -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"`

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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"),
},
}

View File

@ -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,

View File

@ -161,7 +161,7 @@
"outbounds": [
{
"type": "selector",
"tag": "手动切换",
"tag": "节点选择",
"outbounds": ["<all-proxy-tags>", "direct"],
"interrupt_exist_connections": true
},
@ -175,35 +175,35 @@
{
"type": "selector",
"tag": "Microsoft",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"],
"outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择",
"interrupt_exist_connections": true
},
{
"type": "selector",
"tag": "Bilibili",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"],
"outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择",
"interrupt_exist_connections": true
},
{
"type": "selector",
"tag": "Games(全球)",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"],
"outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择",
"interrupt_exist_connections": true
},
{
"type": "selector",
"tag": "Games(中国)",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"],
"outbounds": ["节点选择", "<all-proxy-tags>", "direct"],
"default": "节点选择",
"interrupt_exist_connections": true
},
{
"type": "selector",
"tag": "Bahamut",
"outbounds": ["节点选择", "手动切换", "<all-proxy-tags>", "direct"],
"outbounds": ["节点选择", "<all-proxy-tags>", "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": "节点选择"
}
}
}