Skip to content

Commit

Permalink
Feature: add filter option to proxy group (#2518)
Browse files Browse the repository at this point in the history
  • Loading branch information
yaling888 authored Apr 8, 2023
1 parent 20a521f commit 9b2b7c6
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
24 changes: 21 additions & 3 deletions adapter/outboundgroup/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package outboundgroup
import (
"errors"
"fmt"
"regexp"

"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/adapter/provider"
Expand All @@ -29,6 +30,7 @@ type GroupCommonOption struct {
Interval int `group:"interval,omitempty"`
Lazy bool `group:"lazy,omitempty"`
DisableUDP bool `group:"disable-udp,omitempty"`
Filter string `group:"filter,omitempty"`
}

func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) {
Expand All @@ -45,14 +47,25 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
return nil, errFormat
}

groupName := groupOption.Name
var (
groupName = groupOption.Name
filterReg *regexp.Regexp
)

providers := []types.ProxyProvider{}
if groupOption.Filter != "" {
f, err := regexp.Compile(groupOption.Filter)
if err != nil {
return nil, fmt.Errorf("%s: invalid filter regex: %w", groupName, err)
}
filterReg = f
}

if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
return nil, fmt.Errorf("%s: %w", groupName, errMissProxy)
}

providers := []types.ProxyProvider{}

if len(groupOption.Proxies) != 0 {
ps, err := getProxies(proxyMap, groupOption.Proxies)
if err != nil {
Expand Down Expand Up @@ -94,7 +107,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
}
providers = append(providers, list...)
if filterReg != nil {
pd := provider.NewFilterableProvider(groupName, list, filterReg)
providers = append(providers, pd)
} else {
providers = append(providers, list...)
}
}

var group C.ProxyAdapter
Expand Down
79 changes: 79 additions & 0 deletions adapter/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import (
"time"

"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/singledo"
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider"

"github.com/samber/lo"
"gopkg.in/yaml.v3"
)

var reject = adapter.NewProxy(outbound.NewReject())

const (
ReservedName = "default"
)
Expand Down Expand Up @@ -231,3 +236,77 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co
runtime.SetFinalizer(wrapper, stopCompatibleProvider)
return wrapper, nil
}

var _ types.ProxyProvider = (*FilterableProvider)(nil)

type FilterableProvider struct {
name string
providers []types.ProxyProvider
filterReg *regexp.Regexp
single *singledo.Single
}

func (fp *FilterableProvider) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"name": fp.Name(),
"type": fp.Type().String(),
"vehicleType": fp.VehicleType().String(),
"proxies": fp.Proxies(),
})
}

func (fp *FilterableProvider) Name() string {
return fp.name
}

func (fp *FilterableProvider) HealthCheck() {
}

func (fp *FilterableProvider) Update() error {
return nil
}

func (fp *FilterableProvider) Initial() error {
return nil
}

func (fp *FilterableProvider) VehicleType() types.VehicleType {
return types.Compatible
}

func (fp *FilterableProvider) Type() types.ProviderType {
return types.Proxy
}

func (fp *FilterableProvider) Proxies() []C.Proxy {
elm, _, _ := fp.single.Do(func() (any, error) {
proxies := lo.FlatMap(
fp.providers,
func(item types.ProxyProvider, _ int) []C.Proxy {
return lo.Filter(
item.Proxies(),
func(item C.Proxy, _ int) bool {
return fp.filterReg.MatchString(item.Name())
})
})

if len(proxies) == 0 {
proxies = append(proxies, reject)
}
return proxies, nil
})

return elm.([]C.Proxy)
}

func (fp *FilterableProvider) Touch() {
}

func NewFilterableProvider(name string, providers []types.ProxyProvider, filterReg *regexp.Regexp) *FilterableProvider {
return &FilterableProvider{
name: name,
providers: providers,
filterReg: filterReg,
single: singledo.NewSingle(time.Second * 10),
}
}

0 comments on commit 9b2b7c6

Please sign in to comment.