diff --git a/internal/sub/remark_vars.go b/internal/sub/remark_vars.go index 2a93d31e8..23cc3d6fb 100644 --- a/internal/sub/remark_vars.go +++ b/internal/sub/remark_vars.go @@ -484,6 +484,15 @@ var connectionTokens = map[string]bool{ var displayRemoveTokens = mergeTokenSets(usageInfoTokens, connectionTokens) +// firstLinkOnlyBodyTokens are stripped from every subscription-body link after a +// client's first one: the usage/info tokens plus the per-client EMAIL/USERNAME +// identity. A client app needs the email once, so repeating it on every link of +// the same subscription is noise — show it on the first link only, like traffic. +var firstLinkOnlyBodyTokens = mergeTokenSets(usageInfoTokens, map[string]bool{ + "EMAIL": true, + "USERNAME": true, +}) + func mergeTokenSets(sets ...map[string]bool) map[string]bool { out := make(map[string]bool) for _, set := range sets { @@ -554,7 +563,7 @@ func (s *SubService) effectiveTemplate(email string) string { s.usageShown = map[string]bool{} } if s.usageShown[email] { - return filterRemarkTemplate(translated, usageInfoTokens) + return filterRemarkTemplate(translated, firstLinkOnlyBodyTokens) } s.usageShown[email] = true return translated diff --git a/internal/sub/remark_vars_test.go b/internal/sub/remark_vars_test.go index 3b2b709fe..12125c144 100644 --- a/internal/sub/remark_vars_test.go +++ b/internal/sub/remark_vars_test.go @@ -610,3 +610,32 @@ func TestUsageOnFirstLinkOnly_SingleBracket(t *testing.T) { t.Fatalf("second link must not carry usage: %q", second) } } + +func TestEmailOnFirstLinkOnly(t *testing.T) { + s := &SubService{ + remarkTemplate: "{{INBOUND}} {{EMAIL}}|📊{{TRAFFIC_LEFT}}", + subscriptionBody: true, + usageShown: map[string]bool{}, + } + inbound := &model.Inbound{ + Remark: "DE", + ClientStats: []xray.ClientTraffic{{ + Email: "alice@x", + Enable: true, + Total: 100 * gb, + }}, + } + client := model.Client{Email: "alice@x"} + first := s.genTemplatedRemark(inbound, client, "", "ws") + s.usageShown["alice@x"] = true + second := s.genTemplatedRemark(inbound, client, "", "ws") + if !strings.Contains(first, "alice@x") { + t.Fatalf("first link should carry email: %q", first) + } + if strings.Contains(second, "alice@x") { + t.Fatalf("second link must not carry email: %q", second) + } + if !strings.Contains(second, "DE") { + t.Fatalf("second link should still carry the inbound name: %q", second) + } +}