Liu Song’s Projects


~/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
 }