2024-03-11 05:31:29 -04:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sub2sing-box/constant"
|
|
|
|
"sub2sing-box/model"
|
|
|
|
. "sub2sing-box/util"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
2024-03-11 09:00:13 -04:00
|
|
|
//TODO: 过滤、去重、分组、排序
|
|
|
|
|
2024-03-11 05:31:29 -04:00
|
|
|
var convertCmd = &cobra.Command{
|
|
|
|
Use: "convert",
|
2024-03-11 07:56:50 -04:00
|
|
|
Long: "Convert common proxy to sing-box proxy",
|
|
|
|
Short: "Convert common proxy to sing-box proxy",
|
2024-03-11 05:31:29 -04:00
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
subscriptions, _ := cmd.Flags().GetStringSlice("subscription")
|
|
|
|
proxies, _ := cmd.Flags().GetStringSlice("proxy")
|
|
|
|
template, _ := cmd.Flags().GetString("template")
|
2024-03-11 07:56:50 -04:00
|
|
|
output, _ := cmd.Flags().GetString("output")
|
2024-03-11 09:00:13 -04:00
|
|
|
result := ""
|
|
|
|
var err error
|
|
|
|
|
|
|
|
proxyList, err := ConvertSubscriptionsToSProxy(subscriptions)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, proxy := range proxies {
|
|
|
|
p, err := ConvertCProxyToSProxy(proxy)
|
2024-03-11 05:31:29 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
proxyList = append(proxyList, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
if template != "" {
|
|
|
|
result, err = MergeTemplate(proxyList, template)
|
2024-03-11 07:28:22 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2024-03-11 05:31:29 -04:00
|
|
|
} else {
|
2024-03-11 09:00:13 -04:00
|
|
|
r, err := json.Marshal(proxyList)
|
|
|
|
result = string(r)
|
2024-03-11 05:31:29 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if output != "" {
|
|
|
|
err = os.WriteFile(output, []byte(result), 0666)
|
2024-03-11 05:31:29 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
} else {
|
|
|
|
fmt.Println(string(result))
|
2024-03-11 05:31:29 -04:00
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
|
2024-03-11 05:31:29 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
convertCmd.Flags().StringSliceP("subscription", "s", []string{}, "subscription urls")
|
|
|
|
convertCmd.Flags().StringSliceP("proxy", "p", []string{}, "common proxies")
|
2024-03-11 09:00:13 -04:00
|
|
|
convertCmd.Flags().StringP("template", "t", "", "template file path")
|
2024-03-11 07:56:50 -04:00
|
|
|
convertCmd.Flags().StringP("output", "o", "", "output file path")
|
2024-03-11 09:00:13 -04:00
|
|
|
convertCmd.Flags().StringP("filter", "f", "", "outbound tag filter (support regex)")
|
2024-03-11 05:31:29 -04:00
|
|
|
RootCmd.AddCommand(convertCmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Convert(urls []string, proxies []string) ([]model.Proxy, error) {
|
|
|
|
proxyList := make([]model.Proxy, 0)
|
|
|
|
newProxies, err := ConvertSubscriptionsToSProxy(urls)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
proxyList = append(proxyList, newProxies...)
|
|
|
|
for _, p := range proxies {
|
|
|
|
proxy, err := ConvertCProxyToSProxy(p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
proxyList = append(proxyList, proxy)
|
|
|
|
}
|
|
|
|
return proxyList, nil
|
|
|
|
}
|
|
|
|
|
2024-03-11 09:00:13 -04:00
|
|
|
func MergeTemplate(proxies []model.Proxy, template string) (string, error) {
|
|
|
|
config, err := ReadTemplate(template)
|
|
|
|
proxyTags := make([]string, 0)
|
2024-03-11 05:31:29 -04:00
|
|
|
if err != nil {
|
2024-03-11 09:00:13 -04:00
|
|
|
return "", err
|
2024-03-11 05:31:29 -04:00
|
|
|
}
|
|
|
|
for _, p := range proxies {
|
2024-03-11 09:00:13 -04:00
|
|
|
proxyTags = append(proxyTags, p.Tag)
|
2024-03-11 05:31:29 -04:00
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
ps, err := json.Marshal(&proxies)
|
|
|
|
fmt.Print(string(ps))
|
2024-03-11 05:31:29 -04:00
|
|
|
if err != nil {
|
2024-03-11 09:00:13 -04:00
|
|
|
return "", err
|
2024-03-11 05:31:29 -04:00
|
|
|
}
|
|
|
|
var newOutbounds []model.Outbound
|
|
|
|
err = json.Unmarshal(ps, &newOutbounds)
|
|
|
|
if err != nil {
|
2024-03-11 09:00:13 -04:00
|
|
|
return "", err
|
2024-03-11 07:28:22 -04:00
|
|
|
}
|
|
|
|
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>" {
|
2024-03-11 09:00:13 -04:00
|
|
|
parsedOutbound = append(parsedOutbound, proxyTags...)
|
2024-03-11 07:28:22 -04:00
|
|
|
} else {
|
|
|
|
parsedOutbound = append(parsedOutbound, o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
config.Outbounds[i].Outbounds = parsedOutbound
|
|
|
|
}
|
|
|
|
}
|
2024-03-11 09:00:13 -04:00
|
|
|
config.Outbounds = append(config.Outbounds, newOutbounds...)
|
|
|
|
data, err := json.Marshal(config)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(data), nil
|
2024-03-11 05:31:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertCProxyToSProxy(proxy string) (model.Proxy, error) {
|
|
|
|
for prefix, parseFunc := range constant.ParserMap {
|
|
|
|
if strings.HasPrefix(proxy, prefix) {
|
|
|
|
proxy, err := parseFunc(proxy)
|
|
|
|
if err != nil {
|
|
|
|
return model.Proxy{}, err
|
|
|
|
}
|
|
|
|
return proxy, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return model.Proxy{}, errors.New("Unknown proxy format")
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertCProxyToJson(proxy string) (string, error) {
|
|
|
|
sProxy, err := ConvertCProxyToSProxy(proxy)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
data, err := json.Marshal(&sProxy)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(data), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func FetchSubscription(url string, maxRetryTime int) (string, error) {
|
|
|
|
retryTime := 0
|
|
|
|
var err error
|
|
|
|
for retryTime < maxRetryTime {
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
retryTime++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
retryTime++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return string(data), err
|
|
|
|
}
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertSubscriptionsToSProxy(urls []string) ([]model.Proxy, error) {
|
|
|
|
proxyList := make([]model.Proxy, 0)
|
|
|
|
for _, url := range urls {
|
|
|
|
data, err := FetchSubscription(url, 3)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
proxy, err := DecodeBase64(data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
proxies := strings.Split(proxy, "\n")
|
|
|
|
for _, p := range proxies {
|
|
|
|
for prefix, parseFunc := range constant.ParserMap {
|
|
|
|
if strings.HasPrefix(p, prefix) {
|
|
|
|
proxy, err := parseFunc(p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
proxyList = append(proxyList, proxy)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return proxyList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertSubscriptionsToJson(urls []string) (string, error) {
|
|
|
|
proxyList, err := ConvertSubscriptionsToSProxy(urls)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
result, err := json.Marshal(proxyList)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(result), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReadTemplate(path string) (model.Config, error) {
|
|
|
|
data, err := os.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return model.Config{}, err
|
|
|
|
}
|
|
|
|
var res model.Config
|
|
|
|
err = json.Unmarshal(data, &res)
|
|
|
|
if err != nil {
|
|
|
|
return model.Config{}, err
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|