Bläddra i källkod

fix(sub): drop empty remark segments instead of leaving a stray separator

expandSegment dropped a "|" segment only when its tokens rendered the unlimited
mark, so a segment whose only token resolved to the empty string (a client with
no comment, an unlimited client's expiry date) was kept as bare decoration,
leaving a trailing "|" or a dangling emoji on every share link's remark. Drop a
token-bearing segment whenever none of its tokens produce a real value, while
still keeping pure-literal segments.
MHSanaei 1 dag sedan
förälder
incheckning
aaf17bcfdc
2 ändrade filer med 26 tillägg och 9 borttagningar
  1. 9 9
      internal/sub/remark_vars.go
  2. 17 0
      internal/sub/remark_vars_test.go

+ 9 - 9
internal/sub/remark_vars.go

@@ -124,23 +124,23 @@ func expandRemarkVars(template string, ctx remarkContext) string {
 }
 
 // expandSegment expands one "|" segment and reports whether it should be dropped.
-// It drops only when the segment carries an unlimited (∞) quota/expiry token and
-// no other token in it resolves to a non-empty value — so a segment mixing, say,
-// {{EMAIL}} with {{TRAFFIC_LEFT}} is always kept.
+// A segment that contains tokens is dropped when none of them resolve to a real
+// value — whether because they render the unlimited (∞) mark or the empty string
+// — so it leaves no stray "|" separator or dangling decoration. A segment mixing,
+// say, {{EMAIL}} with {{TRAFFIC_LEFT}} is kept, and a pure-literal segment (no
+// tokens) is always kept.
 func expandSegment(seg string, ctx remarkContext) (string, bool) {
-	hasUnlimited, hasOtherValue := false, false
+	hasToken, hasOtherValue := false, false
 	out := remarkVarRe.ReplaceAllStringFunc(seg, func(m string) string {
+		hasToken = true
 		token := m[2 : len(m)-2]
 		val := remarkVarValue(token, ctx)
-		switch {
-		case unlimitedDropTokens[token] && val == unlimitedMark:
-			hasUnlimited = true
-		case val != "":
+		if val != "" && !(unlimitedDropTokens[token] && val == unlimitedMark) {
 			hasOtherValue = true
 		}
 		return val
 	})
-	return out, hasUnlimited && !hasOtherValue
+	return out, hasToken && !hasOtherValue
 }
 
 func remarkVarValue(token string, ctx remarkContext) string {

+ 17 - 0
internal/sub/remark_vars_test.go

@@ -128,6 +128,23 @@ func TestExpandRemarkVars_DropUnlimitedSegments(t *testing.T) {
 	}
 }
 
+func TestExpandRemarkVars_DropEmptySegments(t *testing.T) {
+	inbound := &model.Inbound{Remark: "host"}
+
+	// A client with no comment: the {{COMMENT}} segment resolves to empty and
+	// must be dropped, not left as a trailing "|".
+	noComment := expandCtx(model.Client{}, xray.ClientTraffic{Enable: true}, inbound)
+	if got := expandRemarkVars("{{INBOUND}}|{{COMMENT}}", noComment); got != "host" {
+		t.Errorf("empty comment segment = %q, want %q (no trailing pipe)", got, "host")
+	}
+
+	// A decorated empty segment (emoji + empty token) drops whole, not leaving
+	// a dangling emoji.
+	if got := expandRemarkVars("{{INBOUND}}|📅{{EXPIRE_DATE}}", noComment); got != "host" {
+		t.Errorf("decorated empty segment = %q, want %q", got, "host")
+	}
+}
+
 func TestClientStatus(t *testing.T) {
 	cases := []struct {
 		name string