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.
This commit is contained in:
MHSanaei
2026-07-15 02:56:42 +02:00
parent 0a1231d70c
commit aaf17bcfdc
2 changed files with 26 additions and 9 deletions
+9 -9
View File
@@ -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
View File
@@ -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