From c004c18d903bcc2b58602f71d411b024fe5ab60d Mon Sep 17 00:00:00 2001 From: Sanaei Date: Mon, 27 Jul 2026 14:24:28 +0200 Subject: [PATCH] fix(sub): keep the client identity on every subscription link (#6098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 876d55f2 put EMAIL/USERNAME in the same first-link-only bucket as the usage tokens, so a client attached to several inbounds got its email on whichever inbound sorted first and bare inbound names on all the rest. With the shipped default template ({{INBOUND}}-{{EMAIL}}|...) that makes every profile after the first indistinguishable between clients — the point of the token. The two are not alike: the usage block repeats identical numbers on every link, while the identity is what tells one imported profile from another. Restore identity on all body links and leave usage first-link-only. Reported again in #6029 and #5659, which asked for the same revert. --- internal/sub/remark_vars.go | 11 +---------- internal/sub/remark_vars_test.go | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/internal/sub/remark_vars.go b/internal/sub/remark_vars.go index 029c7eeb1..529d7f64e 100644 --- a/internal/sub/remark_vars.go +++ b/internal/sub/remark_vars.go @@ -481,15 +481,6 @@ 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 { @@ -560,7 +551,7 @@ func (s *SubService) effectiveTemplate(email string) string { s.usageShown = map[string]bool{} } if s.usageShown[email] { - return filterRemarkTemplate(translated, firstLinkOnlyBodyTokens) + return filterRemarkTemplate(translated, usageInfoTokens) } s.usageShown[email] = true return translated diff --git a/internal/sub/remark_vars_test.go b/internal/sub/remark_vars_test.go index b291a0ed5..7d66eb333 100644 --- a/internal/sub/remark_vars_test.go +++ b/internal/sub/remark_vars_test.go @@ -386,8 +386,8 @@ func TestIdentityTokenBodyVsDisplay(t *testing.T) { body := &SubService{remarkTemplate: tmpl, subscriptionBody: true, usageShown: map[string]bool{}} _ = body.genTemplatedRemark(inbound, client, "", "ws") // first link consumes the usage block - if second := body.genTemplatedRemark(inbound, client, "", "ws"); strings.Contains(second, "john@x") { - t.Fatalf("repeat body link %q must drop the identity token", second) + if second := body.genTemplatedRemark(inbound, client, "", "ws"); !strings.Contains(second, "john@x") { + t.Fatalf("repeat body link %q must keep the identity token", second) } display := &SubService{remarkTemplate: tmpl, subscriptionBody: false} @@ -624,7 +624,10 @@ func TestUsageOnFirstLinkOnly_SingleBracket(t *testing.T) { } } -func TestEmailOnFirstLinkOnly(t *testing.T) { +// Every link of a subscription carries the client's identity, because that is +// what tells one imported profile from another in the client app. Only the +// usage block, which is identical on all of them, is first-link-only (#6098). +func TestEmailOnEveryLink(t *testing.T) { s := &SubService{ remarkTemplate: "{{INBOUND}} {{EMAIL}}|📊{{TRAFFIC_LEFT}}", subscriptionBody: true, @@ -640,15 +643,22 @@ func TestEmailOnFirstLinkOnly(t *testing.T) { } 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) + third := s.genTemplatedRemark(inbound, client, "", "ws") + for i, remark := range []string{first, second, third} { + if !strings.Contains(remark, "alice@x") { + t.Fatalf("link %d must carry the client email: %q", i+1, remark) + } + if !strings.Contains(remark, "DE") { + t.Fatalf("link %d must carry the inbound name: %q", i+1, remark) + } } - if strings.Contains(second, "alice@x") { - t.Fatalf("second link must not carry email: %q", second) + if !strings.Contains(first, "📊") { + t.Fatalf("first link should carry the usage block: %q", first) } - if !strings.Contains(second, "DE") { - t.Fatalf("second link should still carry the inbound name: %q", second) + for i, remark := range []string{second, third} { + if strings.Contains(remark, "📊") { + t.Fatalf("repeat link %d must still drop the usage block: %q", i+2, remark) + } } }