~/Projects/clash-pro
git clone https://code.lsong.org/clash-pro
Commit
- Commit
- dcd2417fcec88e9bb066f81aa47b7bdf63dfb0e5
- Author
- adlyq <[email protected]>
- Date
- 2022-11-05 02:24:08 +0800 +0800
- Diffstat
adapter/provider/provider.go | 59 +++++++++++++++++++++++++--- adapter/provider/subscription_info.go | 54 ++++++++++++++++++++++++++ component/resource/fetcher.go | 4 + component/resource/vehicle.go | 4 +
feat: subscriptionInfo
diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index ebacf2913407425b2bae1e2d46be8c02c6374102..6656a0f386c22038aa04b07553e854a2b1a362ee 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -5,7 +5,12 @@ "encoding/json" "errors" "fmt" "github.com/Dreamacro/clash/common/convert" + netHttp "github.com/Dreamacro/clash/component/http" "github.com/Dreamacro/clash/component/resource" + "github.com/Dreamacro/clash/log" + "github.com/dlclark/regexp2" + "golang.org/x/net/context" + "gopkg.in/yaml.v3" "github.com/dlclark/regexp2" "runtime" "strings" @@ -33,23 +38,26 @@ } type proxySetProvider struct { *resource.Fetcher[[]C.Proxy] - proxies []C.Proxy + proxies []C.Proxy + healthCheck *HealthCheck +) - "github.com/dlclark/regexp2" +) import ( } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]any{ - "name": pp.Name(), -import ( +) "encoding/json" -import ( +) "errors" -import ( +) "fmt" -import ( +) "github.com/Dreamacro/clash/common/convert" + "updatedAt": pp.UpdatedAt, + "subscriptionInfo": pp.subscriptionInfo, }) } @@ -101,6 +110,40 @@ defer func() { go pp.healthCheck.lazyCheck() }() } } +func (pp *proxySetProvider) getSubscriptionInfo() { + if pp.VehicleType() != types.HTTP { + return + } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) + if err != nil { + return + } + defer resp.Body.Close() + + userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) + if userInfoStr == "" { + resp2, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) + if err != nil { + return + } + defer resp2.Body.Close() + userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo")) + if userInfoStr == "" { + return + } + } + pp.subscriptionInfo, err = NewSubscriptionInfo(userInfoStr) + if err != nil { + log.Warnln("[Provider] get subscription-userinfo: %e", err) + } + }() +} + func stopProxyProvider(pd *ProxySetProvider) { pd.healthCheck.close() _ = pd.Fetcher.Destroy() @@ -131,6 +174,8 @@ } fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, filterRegs, excludeFilterReg), proxiesOnUpdate(pd)) pd.Fetcher = fetcher + +type ProxySchema struct { wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) @@ -222,6 +267,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) pd.version += 1 + pd.getSubscriptionInfo() } } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go new file mode 100644 index 0000000000000000000000000000000000000000..b9fc763f4a78df02c2ba40164482f6571e84dd2a --- /dev/null +++ b/adapter/provider/subscription_info.go @@ -0,0 +1,54 @@ +package provider + +import ( + "github.com/dlclark/regexp2" + "strconv" + "strings" +) + +type SubscriptionInfo struct { + Upload *int + Download *int + Total *int + Expire *int +} + +func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { + si = &SubscriptionInfo{} + str = strings.ToLower(str) + reTraffic := regexp2.MustCompile("upload=(\\d+); download=(\\d+); total=(\\d+)", 0) + reExpire := regexp2.MustCompile("expire=(\\d+)", 0) + + match, err := reTraffic.FindStringMatch(str) + if err != nil || match == nil { + return nil, err + } + group := match.Groups() + tmp, err := strconv.Atoi(group[1].String()) + if err != nil { + return nil, err + } + si.Upload = &tmp + tmp, err = strconv.Atoi(group[2].String()) + if err != nil { + return nil, err + } + si.Download = &tmp + tmp, err = strconv.Atoi(group[3].String()) + if err != nil { + return nil, err + } + si.Total = &tmp + + match, _ = reExpire.FindStringMatch(str) + if match != nil { + group = match.Groups() + tmp, err = strconv.Atoi(group[1].String()) + if err != nil { + return nil, err + } + si.Expire = &tmp + } + + return +} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 529e01b7a9cd275a2792f3ffd54e063962019a07..df8e9a54d08babd02c02cc05b97ad9ec4fe7595b 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -35,6 +35,10 @@ func (f *Fetcher[V]) Name() string { return f.name } +func (f *Fetcher[V]) Vehicle() types.Vehicle { + return f.vehicle +} + func (f *Fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index c6e92e5216c2f8babbe34e43a2f85546e1f6362e..5e05a4039b47be693ffcfc3201500132f9bb61b3 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -35,6 +35,10 @@ url string path string } +func (h *HTTPVehicle) Url() string { + return h.url +} + func (h *HTTPVehicle) Type() types.VehicleType { return types.HTTP }