feat(proxy): enhance proxy group handling and sorting logic

This commit is contained in:
Chang lue Tsen 2025-07-16 14:17:36 -04:00 committed by Leif Draven
parent 979e39b9e5
commit 97024dd1df
6 changed files with 113 additions and 37 deletions

View File

@ -9,11 +9,11 @@ import (
) )
const ( const (
RelayModeNone = "none" RelayModeNone = "none"
RelayModeAll = "all" RelayModeAll = "all"
RelayModeRandom = "random" RelayModeRandom = "random"
RuleGroupType = "ban" RuleGroupTypeBan = "ban"
RuleGroupAuto = "auto" RuleGroupTypeAuto = "auto"
) )
type ServerFilter struct { type ServerFilter struct {

View File

@ -26,17 +26,21 @@ type Adapter struct {
func NewAdapter(cfg *Config) *Adapter { func NewAdapter(cfg *Config) *Adapter {
// 转换服务器列表 // 转换服务器列表
proxies := adapterProxies(cfg.Nodes) proxies := adapterProxies(cfg.Nodes)
// 生成代理组 defaultGroup := FindDefaultGroup(cfg.Rules)
proxyGroup, nodes := generateProxyGroup(proxies)
// 转换规则组 // 转换规则组
g, r := adapterRules(cfg.Rules) g, r := adapterRules(cfg.Rules)
// 生成代理组
proxyGroup, nodes := generateProxyGroup(proxies)
// 加入兜底节点 // 加入兜底节点
for i, group := range g { for i, group := range g {
if group.Direct {
continue
}
if len(group.Proxies) == 0 { if len(group.Proxies) == 0 {
p := append([]string{"智能线路"}, nodes...) p := append([]string{"Auto Select", "Selection"}, nodes...)
g[i].Proxies = append(p, "REJECT", "DIRECT") g[i].Proxies = append(p, "DIRECT")
} }
} }
@ -44,12 +48,14 @@ func NewAdapter(cfg *Config) *Adapter {
proxyGroup = RemoveEmptyGroup(append(proxyGroup, g...)) proxyGroup = RemoveEmptyGroup(append(proxyGroup, g...))
// 处理标签 // 处理标签
proxyGroup = adapterTags(cfg.Tags, proxyGroup) proxyGroup = adapterTags(cfg.Tags, proxyGroup)
return &Adapter{ return &Adapter{
Adapter: proxy.Adapter{ Adapter: proxy.Adapter{
Proxies: proxies, Proxies: proxies,
Group: proxyGroup, Group: SortGroups(proxyGroup, defaultGroup),
Rules: r, Rules: r,
Nodes: nodes, Nodes: nodes,
Default: defaultGroup,
}, },
} }
} }

View File

@ -45,7 +45,7 @@ func (c *Clash) Build(uuid string) ([]byte, error) {
}) })
} }
rawConfig.ProxyGroups = groups rawConfig.ProxyGroups = groups
rawConfig.Rules = append(c.Rules, "MATCH,手动选择") rawConfig.Rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
return yaml.Marshal(&rawConfig) return yaml.Marshal(&rawConfig)
} }

View File

@ -26,6 +26,7 @@ type Group struct {
Proxies []string Proxies []string
URL string URL string
Interval int Interval int
Direct bool
} }
type GroupType string type GroupType string

View File

@ -79,7 +79,7 @@ func BuildSingbox(adapter proxy.Adapter, uuid string) ([]byte, error) {
rawConfig["outbounds"] = proxies rawConfig["outbounds"] = proxies
route := RouteOptions{ route := RouteOptions{
Final: "手动选择", Final: adapter.Default,
Rules: []Rule{ Rules: []Rule{
{ {
Inbound: []string{ Inbound: []string{
@ -114,7 +114,7 @@ func BuildSingbox(adapter proxy.Adapter, uuid string) ([]byte, error) {
}, },
{ {
ClashMode: "global", ClashMode: "global",
Outbound: "手动选择", Outbound: adapter.Default,
}, },
{ {
IPIsPrivate: true, IPIsPrivate: true,

View File

@ -99,11 +99,29 @@ func addProxyToGroup(proxyName, groupName string, groups []proxy.Group) []proxy.
func adapterRules(groups []*server.RuleGroup) (proxyGroup []proxy.Group, rules []string) { func adapterRules(groups []*server.RuleGroup) (proxyGroup []proxy.Group, rules []string) {
for _, group := range groups { for _, group := range groups {
proxyGroup = append(proxyGroup, proxy.Group{ switch group.Type {
Name: group.Name, case server.RuleGroupTypeBan:
Type: proxy.GroupTypeSelect, proxyGroup = append(proxyGroup, proxy.Group{
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")), Name: group.Name,
}) Type: proxy.GroupTypeSelect,
Proxies: []string{"REJECT", "DIRECT"},
Direct: true,
})
case server.RuleGroupTypeAuto:
proxyGroup = append(proxyGroup, proxy.Group{
Name: group.Name,
Type: proxy.GroupTypeURLTest,
URL: "https://www.gstatic.com/generate_204",
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")),
})
default:
proxyGroup = append(proxyGroup, proxy.Group{
Name: group.Name,
Type: proxy.GroupTypeSelect,
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")),
})
}
rules = append(rules, strings.Split(group.Rules, "\n")...) rules = append(rules, strings.Split(group.Rules, "\n")...)
} }
return return
@ -122,29 +140,26 @@ func adapterTags(tags map[string][]*server.Server, group []proxy.Group) (proxyGr
} }
func generateProxyGroup(servers []proxy.Proxy) (proxyGroup []proxy.Group, nodes []string) { func generateProxyGroup(servers []proxy.Proxy) (proxyGroup []proxy.Group, nodes []string) {
proxyGroup = append(proxyGroup, proxy.Group{
Name: "Auto Select",
Type: proxy.GroupTypeURLTest,
Proxies: make([]string, 0),
URL: "https://www.gstatic.com/generate_204",
Interval: 300,
})
// 设置手动选择分组 // 设置手动选择分组
proxyGroup = append(proxyGroup, []proxy.Group{ proxyGroup = append(proxyGroup, proxy.Group{
{ Name: "Selection",
Name: "智能线路", Type: proxy.GroupTypeSelect,
Type: proxy.GroupTypeURLTest, Proxies: []string{"Auto Select"},
Proxies: make([]string, 0), })
URL: "https://www.gstatic.com/generate_204",
Interval: 300,
},
{
Name: "手动选择",
Type: proxy.GroupTypeSelect,
Proxies: []string{"智能线路"},
},
}...)
for _, node := range servers { for _, node := range servers {
proxyGroup = addProxyToGroup(node.Name, "智能线路", proxyGroup) proxyGroup = addProxyToGroup(node.Name, "Auto Select", proxyGroup)
proxyGroup = addProxyToGroup(node.Name, "手动选择", proxyGroup) proxyGroup = addProxyToGroup(node.Name, "Selection", proxyGroup)
nodes = append(nodes, node.Name) nodes = append(nodes, node.Name)
} }
proxyGroup = addProxyToGroup("DIRECT", "手动选择", proxyGroup)
return proxyGroup, tool.RemoveDuplicateElements(nodes...) return proxyGroup, tool.RemoveDuplicateElements(nodes...)
} }
@ -206,6 +221,7 @@ func RemoveEmptyString(arr []string) []string {
return result return result
} }
// RemoveEmptyGroup removes empty groups from the provided slice of proxy groups.
func RemoveEmptyGroup(arr []proxy.Group) []proxy.Group { func RemoveEmptyGroup(arr []proxy.Group) []proxy.Group {
var result []proxy.Group var result []proxy.Group
var removeNames []string var removeNames []string
@ -221,3 +237,56 @@ func RemoveEmptyGroup(arr []proxy.Group) []proxy.Group {
} }
return result return result
} }
// FindDefaultGroup finds the default rule group from a list of rule groups.
func FindDefaultGroup(groups []*server.RuleGroup) string {
for _, group := range groups {
if group.Default {
return group.Name
}
}
return "智能线路"
}
// SortGroups sorts the provided slice of proxy groups by their names.
func SortGroups(groups []proxy.Group, defaultName string) []proxy.Group {
var sortedGroups []proxy.Group
var selectedGroup proxy.Group
// 在所有分组找到默认分组并将他放到第一个
for _, group := range groups {
if group.Name == defaultName {
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName, "REJECT")
sortedGroups = append([]proxy.Group{group}, sortedGroups...)
continue
} else if group.Name == "Selection" {
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName)
selectedGroup = group
continue
} else if group.Name == "Auto Select" {
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName, group.Name)
sortedGroups = append([]proxy.Group{group}, sortedGroups...)
continue
}
sortedGroups = append(sortedGroups, group)
}
// 将手动选择分组放到最后
if selectedGroup.Name != "" {
sortedGroups = append(sortedGroups, selectedGroup)
}
return sortedGroups
}
// RemoveElementByName removes elements from the slice of proxy groups by their names.
func RemoveElementByName(groups []proxy.Group, names ...string) []proxy.Group {
for i := 0; i < len(groups); i++ {
for _, name := range names {
if groups[i].Name == name {
groups = append(groups[:i], groups[i+1:]...)
i-- // Adjust index after removal
break // Exit inner loop to avoid index out of range
}
}
}
return groups
}