diff --git a/go.mod b/go.mod index fd3f8817..fa8cf5eb 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v3 v3.23.6 github.com/xtls/xray-core v1.8.3 + github.com/yaa110/go-persian-calendar v1.1.5 go.uber.org/atomic v1.11.0 golang.org/x/text v0.11.0 google.golang.org/grpc v1.56.2 diff --git a/go.sum b/go.sum index 0c1e64aa..c556ce5f 100644 --- a/go.sum +++ b/go.sum @@ -160,6 +160,8 @@ github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 h1:AMyzgjkh54WocjQSlC github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y= github.com/xtls/xray-core v1.8.3 h1:lxaVklPjLKqUU4ua4qH8SBaRcAaNHlH+LmXOx0U/Ejg= github.com/xtls/xray-core v1.8.3/go.mod h1:i7t4JFnq828P2+XK0XjGQ8W9x78iu+EJ7jI4l3sonIw= +github.com/yaa110/go-persian-calendar v1.1.5 h1:EUipRRhzE6bR2NZaSyZ5BEOP46LGbUjzQgdC+Ivrbe4= +github.com/yaa110/go-persian-calendar v1.1.5/go.mod h1:qtnmHCS9u1EiwzzSCSttGoxD5NfV9ZMzymxFCBYmqfg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= diff --git a/sub/subService.go b/sub/subService.go index 6d787023..79cc881e 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -3,6 +3,8 @@ package sub import ( "encoding/base64" "fmt" + "time" + ptime "github.com/yaa110/go-persian-calendar" "net/url" "strings" "x-ui/database" @@ -10,7 +12,7 @@ import ( "x-ui/logger" "x-ui/web/service" "x-ui/xray" - + "x-ui/util/common" "github.com/goccy/go-json" ) @@ -55,7 +57,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, []string, err } for _, client := range clients { if client.Enable && client.SubID == subId { - link := s.getLink(inbound, client.Email) + link := s.getLink(inbound, client.Email,client.ExpiryTime) result = append(result, link) clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email)) } @@ -121,25 +123,29 @@ func (s *SubService) getFallbackMaster(dest string) (*model.Inbound, error) { return inbound, nil } -func (s *SubService) getLink(inbound *model.Inbound, email string) string { +func (s *SubService) getLink(inbound *model.Inbound, email string, expiryTime int64) string { switch inbound.Protocol { case "vmess": - return s.genVmessLink(inbound, email) + return s.genVmessLink(inbound, email, expiryTime) case "vless": - return s.genVlessLink(inbound, email) + return s.genVlessLink(inbound, email, expiryTime) case "trojan": - return s.genTrojanLink(inbound, email) + return s.genTrojanLink(inbound, email, expiryTime) case "shadowsocks": - return s.genShadowsocksLink(inbound, email) + return s.genShadowsocksLink(inbound, email, expiryTime) } return "" } -func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { +func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTime int64) string { if inbound.Protocol != model.VMess { return "" } - remark := fmt.Sprintf("%s-%s", inbound.Remark, email) + + remainedTraffic := s.getRemainedTraffic(email) + expiryTimeString := getExpiryTime(expiryTime) + + remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString) obj := map[string]interface{}{ "v": "2", "ps": remark, @@ -256,7 +262,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) } -func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { +func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTime int64) string { address := s.address if inbound.Protocol != model.VLESS { return "" @@ -449,7 +455,10 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { // Set the new query values on the URL url.RawQuery = q.Encode() - remark := fmt.Sprintf("%s-%s", inbound.Remark, email) + remainedTraffic := s.getRemainedTraffic(email) + expiryTimeString := getExpiryTime(expiryTime) + + remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString) if len(domains) > 0 { links := "" @@ -468,7 +477,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { return url.String() } -func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string { +func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryTime int64) string { address := s.address if inbound.Protocol != model.Trojan { return "" @@ -658,7 +667,10 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string // Set the new query values on the URL url.RawQuery = q.Encode() - remark := fmt.Sprintf("%s-%s", inbound.Remark, email) + remainedTraffic := s.getRemainedTraffic(email) + expiryTimeString := getExpiryTime(expiryTime) + + remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString) if len(domains) > 0 { links := "" @@ -678,7 +690,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string return url.String() } -func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string { +func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, expiryTime int64) string { address := s.address if inbound.Protocol != model.Shadowsocks { return "" @@ -697,7 +709,11 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st } } encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password) - remark := fmt.Sprintf("%s-%s", inbound.Remark, clients[clientIndex].Email) + + remainedTraffic := s.getRemainedTraffic(clients[clientIndex].Email) + expiryTimeString := getExpiryTime(expiryTime) + + remark := fmt.Sprintf("%s: %s- %s", clients[clientIndex].Email, remainedTraffic ,expiryTimeString) return fmt.Sprintf("ss://%s@%s:%d#%s", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port, remark) } @@ -742,3 +758,38 @@ func searchHost(headers interface{}) string { return "" } + +func getExpiryTime(expiryTime int64) string{ + now := time.Now().Unix() + expiryString := "" + + timeDifference := expiryTime/1000 - now + + if expiryTime == 0 { + expiryString = "♾ ⏳" + } else if timeDifference > 172800 { + expiryString = fmt.Sprintf("%s ⏳", ptime.Unix((expiryTime / 1000), 0).Format("yy-MM-dd hh:mm")) + } else if expiryTime < 0 { + expiryString = fmt.Sprintf("%d ⏳", expiryTime/-86400000) + } else { + expiryString = fmt.Sprintf("%s %d ⏳", "ساعت", timeDifference/3600) + } + + return expiryString +} + +func (s *SubService) getRemainedTraffic( email string) string{ + traffic, err := s.inboundService.GetClientTrafficByEmail(email) + if err != nil { + logger.Warning(err) + } + + remainedTraffic := "" + if traffic.Total == 0 { + remainedTraffic = "♾ 📊" + } else { + remainedTraffic = fmt.Sprintf("%s%s" ,common.FormatTraffic(traffic.Total-(traffic.Up+traffic.Down)), "📊") + } + + return remainedTraffic +} \ No newline at end of file