Jelajahi Sumber

refactor(mtproto): manage ad-tags per client only

The inbound-level ad-tag duplicated the per-client override for no
gain: the fork's global tag applied to every secret anyway, so one
value had two homes and they could drift. The inbound form field, the
settings key, and the global ad-tag in the generated config and in the
PUT /secrets body are gone; the tag is set on each client instead.
Existing inbound-level values are intentionally not migrated; a
leftover settings key is stripped on the next save.
MHSanaei 13 jam lalu
induk
melakukan
ad7a0f8164

+ 0 - 10
frontend/src/pages/inbounds/form/protocols/mtproto.tsx

@@ -2,8 +2,6 @@ import { useTranslation } from 'react-i18next';
 import { Form, Input, InputNumber, Select, Switch } from 'antd';
 
 import { useOutboundTags } from '@/api/queries/useOutboundTags';
-import { MtprotoInboundSettingsSchema } from '@/schemas/protocols/inbound/mtproto';
-import { antdRule } from '@/utils/zodForm';
 
 export default function MtprotoFields() {
   const { t } = useTranslation();
@@ -87,14 +85,6 @@ export default function MtprotoFields() {
           />
         </Form.Item>
       )}
-      <Form.Item
-        name={['settings', 'adTag']}
-        label={t('pages.inbounds.form.mtgAdTag')}
-        tooltip={t('pages.inbounds.form.mtgAdTagHint')}
-        rules={[antdRule(MtprotoInboundSettingsSchema.shape.adTag, t)]}
-      >
-        <Input allowClear placeholder="0123456789abcdef0123456789abcdef" />
-      </Form.Item>
       <Form.Item
         name={['settings', 'publicIpv4']}
         label={t('pages.inbounds.form.mtgPublicIpv4')}

+ 2 - 9
frontend/src/schemas/protocols/inbound/mtproto.ts

@@ -57,15 +57,8 @@ export const MtprotoInboundSettingsSchema = z.object({
   routeThroughXray: z.boolean().optional(),
   outboundTag: z.string().optional(),
   routeXrayPort: z.number().int().min(0).max(65535).optional(),
-  // A 32-hex Telegram advertising tag: when set, mtg routes clients through
-  // Telegram middle proxies so a sponsored channel appears in their chat list.
-  // publicIpv4/publicIpv6 pin this server's reachable address the middle proxy
-  // needs; leave them blank to let mtg auto-detect it.
-  adTag: z
-    .string()
-    .regex(/^[0-9a-fA-F]{32}$/, 'pages.inbounds.form.mtgAdTagInvalid')
-    .or(z.literal(''))
-    .optional(),
+  // publicIpv4/publicIpv6 pin this server's reachable address the Telegram
+  // middle proxy needs when clients carry ad-tags; blank = mtg auto-detects.
   publicIpv4: z.string().optional(),
   publicIpv6: z.string().optional(),
 });

+ 20 - 0
internal/database/model/model.go

@@ -493,6 +493,26 @@ func StripMtprotoInboundSecret(settings string) (string, bool) {
 	return string(out), true
 }
 
+// StripMtprotoInboundAdTag drops the dead inbound-level `adTag` — tags live on clients.
+func StripMtprotoInboundAdTag(settings string) (string, bool) {
+	if settings == "" {
+		return settings, false
+	}
+	var parsed map[string]any
+	if err := json.Unmarshal([]byte(settings), &parsed); err != nil {
+		return settings, false
+	}
+	if _, ok := parsed["adTag"]; !ok {
+		return settings, false
+	}
+	delete(parsed, "adTag")
+	out, err := json.MarshalIndent(parsed, "", "  ")
+	if err != nil {
+		return settings, false
+	}
+	return string(out), true
+}
+
 // mtprotoSecretDomain extracts the FakeTLS domain embedded in the tail of a
 // secret, returning an empty string when the secret is malformed. Each mtproto
 // client carries its own domain inside its secret, so healing preserves it

+ 22 - 0
internal/database/model/model_mtproto_test.go

@@ -25,6 +25,28 @@ func TestGenerateFakeTLSSecret(t *testing.T) {
 	}
 }
 
+func TestStripMtprotoInboundAdTag(t *testing.T) {
+	in := `{"adTag":"0123456789abcdef0123456789abcdef","clients":[{"email":"a","adTag":"fedcba9876543210fedcba9876543210"}]}`
+	out, changed := StripMtprotoInboundAdTag(in)
+	if !changed {
+		t.Fatal("expected the inbound-level adTag to be stripped")
+	}
+	var parsed map[string]any
+	if err := json.Unmarshal([]byte(out), &parsed); err != nil {
+		t.Fatalf("unmarshal: %v", err)
+	}
+	if _, ok := parsed["adTag"]; ok {
+		t.Fatalf("adTag key must be removed, got %s", out)
+	}
+	clients := parsed["clients"].([]any)
+	if clients[0].(map[string]any)["adTag"] != "fedcba9876543210fedcba9876543210" {
+		t.Fatalf("client adTag must be preserved, got %s", out)
+	}
+	if _, changed2 := StripMtprotoInboundAdTag(out); changed2 {
+		t.Fatal("second strip must be a no-op")
+	}
+}
+
 func TestStripMtprotoInboundSecret(t *testing.T) {
 	// A multi-client inbound that still carries a dead inbound-level secret has
 	// it removed while the clients (and every other key) survive untouched.

+ 7 - 17
internal/mtproto/manager.go

@@ -54,13 +54,9 @@ type Instance struct {
 	// fair-share algorithm; zero disables throttling.
 	ThrottleMaxConnections int
 
-	// AdTag is a 32-hex Telegram advertising tag; when set, mtg routes clients
-	// through Telegram middle proxies so a sponsored channel shows in their chat
-	// list. It is part of the reloadable secret config, so a change is applied
-	// via /reload without dropping connections. PublicIPv4/PublicIPv6 pin the
-	// proxy's reachable address the middle proxy needs; they are omitted when
-	// empty so mtg auto-detects, and a change forces a restart.
-	AdTag      string
+	// PublicIPv4/PublicIPv6 pin the proxy's reachable address the Telegram
+	// middle proxy needs when clients carry advertising tags; they are omitted
+	// when empty so mtg auto-detects, and a change forces a restart.
 	PublicIPv4 string
 	PublicIPv6 string
 
@@ -104,15 +100,15 @@ func (inst Instance) structuralFingerprint() string {
 // secretsFingerprint identifies the reloadable secret config regardless of
 // client order, so a reordered clients array in the stored settings does not
 // read as a change. It moves whenever a client is added, removed, disabled,
-// re-keyed, or re-tagged, or the global advertising tag changes — all of which
-// mtg applies in place without dropping connections.
+// re-keyed, or re-tagged — all of which mtg applies in place without dropping
+// connections.
 func (inst Instance) secretsFingerprint() string {
 	pairs := make([]string, 0, len(inst.Secrets))
 	for _, e := range inst.Secrets {
 		pairs = append(pairs, e.Name+"="+e.Secret+";tag="+e.AdTag)
 	}
 	slices.Sort(pairs)
-	return "adtag=" + inst.AdTag + "|" + strings.Join(pairs, "|")
+	return strings.Join(pairs, "|")
 }
 
 // Traffic is a per-client traffic delta scraped from an mtg /stats endpoint. Tag
@@ -183,7 +179,6 @@ func InstanceFromInbound(ib *model.Inbound) (Instance, bool) {
 		ThrottleMaxConnections int    `json:"throttleMaxConnections"`
 		RouteThroughXray       bool   `json:"routeThroughXray"`
 		RouteXrayPort          int    `json:"routeXrayPort"`
-		AdTag                  string `json:"adTag"`
 		PublicIPv4             string `json:"publicIpv4"`
 		PublicIPv6             string `json:"publicIpv6"`
 		Clients                []struct {
@@ -221,7 +216,6 @@ func InstanceFromInbound(ib *model.Inbound) (Instance, bool) {
 		ThrottleMaxConnections: parsed.ThrottleMaxConnections,
 		RouteThroughXray:       parsed.RouteThroughXray,
 		XrayRoutePort:          parsed.RouteXrayPort,
-		AdTag:                  usableAdTag(parsed.AdTag),
 		PublicIPv4:             strings.TrimSpace(parsed.PublicIPv4),
 		PublicIPv6:             strings.TrimSpace(parsed.PublicIPv6),
 	}, true
@@ -490,9 +484,6 @@ func renderConfig(inst Instance, apiPort int, apiToken string) string {
 	if apiToken != "" {
 		fmt.Fprintf(&b, "api-token = %q\n", apiToken)
 	}
-	if inst.AdTag != "" {
-		fmt.Fprintf(&b, "ad-tag = %q\n", inst.AdTag)
-	}
 	if inst.PublicIPv4 != "" {
 		fmt.Fprintf(&b, "public-ipv4 = %q\n", inst.PublicIPv4)
 	}
@@ -564,7 +555,6 @@ type secretPutEntry struct {
 
 type secretsPutBody struct {
 	Secrets map[string]secretPutEntry `json:"secrets"`
-	AdTag   string                    `json:"ad_tag,omitempty"`
 }
 
 func secretsPayload(inst Instance) secretsPutBody {
@@ -572,7 +562,7 @@ func secretsPayload(inst Instance) secretsPutBody {
 	for _, e := range inst.Secrets {
 		secrets[e.Name] = secretPutEntry{Secret: e.Secret, AdTag: e.AdTag}
 	}
-	return secretsPutBody{Secrets: secrets, AdTag: inst.AdTag}
+	return secretsPutBody{Secrets: secrets}
 }
 
 // newAPIToken mints the bearer token one mtg process and its manager share for

+ 3 - 4
internal/mtproto/manager_reload_test.go

@@ -128,7 +128,6 @@ func TestApplySecrets(t *testing.T) {
 			inst := mtgInst(1,
 				SecretEntry{Name: "alice", Secret: "ee01"},
 				SecretEntry{Name: "bob", Secret: "ee02", AdTag: "fedcba9876543210fedcba9876543210"})
-			inst.AdTag = "0123456789abcdef0123456789abcdef"
 			if got := applySecrets(serverPort(t, srv), "sesame", inst); got != tc.want {
 				t.Fatalf("applySecrets = %v, want %v", got, tc.want)
 			}
@@ -138,11 +137,11 @@ func TestApplySecrets(t *testing.T) {
 			if gotAuth != "Bearer sesame" {
 				t.Fatalf("expected the bearer token on the request, got %q", gotAuth)
 			}
-			if gotBody.Secrets["alice"].Secret != "ee01" || gotBody.AdTag != "0123456789abcdef0123456789abcdef" {
-				t.Fatalf("payload must carry the secret and ad-tag: %+v", gotBody)
+			if gotBody.Secrets["alice"].Secret != "ee01" {
+				t.Fatalf("payload must carry the secret: %+v", gotBody)
 			}
 			if gotBody.Secrets["alice"].AdTag != "" || gotBody.Secrets["bob"].AdTag != "fedcba9876543210fedcba9876543210" {
-				t.Fatalf("payload must carry per-client ad-tag overrides only where set: %+v", gotBody)
+				t.Fatalf("payload must carry per-client ad-tags only where set: %+v", gotBody)
 			}
 		})
 	}

+ 10 - 15
internal/mtproto/manager_test.go

@@ -40,10 +40,7 @@ func TestInstanceFromInbound(t *testing.T) {
 		t.Fatalf("a valid secret must be preserved, got %q", inst.Secrets[0].Secret)
 	}
 	if inst.Secrets[0].AdTag != "fedcba9876543210fedcba9876543210" {
-		t.Fatalf("the client ad-tag override must be parsed, got %q", inst.Secrets[0].AdTag)
-	}
-	if inst.AdTag != "0123456789abcdef0123456789abcdef" {
-		t.Fatalf("the inbound ad-tag must be trimmed and kept, got %q", inst.AdTag)
+		t.Fatalf("the client ad-tag must be parsed, got %q", inst.Secrets[0].AdTag)
 	}
 	if inst.Port != 8443 || inst.Id != 3 {
 		t.Fatalf("bad instance %+v", inst)
@@ -70,15 +67,13 @@ func TestInstanceFromInbound(t *testing.T) {
 		t.Fatal("an inbound with no active secret should not produce an instance")
 	}
 
-	badTags := &model.Inbound{Protocol: model.MTProto, Settings: `{"adTag":"nope",` +
-		`"clients":[{"email":"x","secret":"ee00","adTag":"deadbeef","enable":true}]}`}
+	badTags := &model.Inbound{Protocol: model.MTProto, Settings: `{"clients":[{"email":"x","secret":"ee00","adTag":"deadbeef","enable":true}]}`}
 	badInst, ok := InstanceFromInbound(badTags)
 	if !ok {
-		t.Fatal("expected a usable instance despite malformed ad tags")
+		t.Fatal("expected a usable instance despite a malformed ad tag")
 	}
-	if badInst.AdTag != "" || badInst.Secrets[0].AdTag != "" {
-		t.Fatalf("malformed ad tags must be dropped so the generated config stays valid, got global=%q client=%q",
-			badInst.AdTag, badInst.Secrets[0].AdTag)
+	if badInst.Secrets[0].AdTag != "" {
+		t.Fatalf("a malformed ad tag must be dropped so the generated config stays valid, got %q", badInst.Secrets[0].AdTag)
 	}
 }
 
@@ -116,7 +111,6 @@ func TestRenderConfig(t *testing.T) {
 		Debug: true, ProxyProtocolListener: true, PreferIP: "only-ipv6",
 		FrontingIP: "127.0.0.1", FrontingPort: 9443, FrontingProxyProtocol: true,
 		ThrottleMaxConnections: 5000,
-		AdTag:                  "0123456789abcdef0123456789abcdef",
 		PublicIPv4:             "1.2.3.4",
 		PublicIPv6:             "2001:db8::1",
 	}, 6000, "sesame")
@@ -125,7 +119,6 @@ func TestRenderConfig(t *testing.T) {
 		"proxy-protocol-listener = true\n",
 		`prefer-ip = "only-ipv6"`,
 		`api-token = "sesame"`,
-		`ad-tag = "0123456789abcdef0123456789abcdef"`,
 		`public-ipv4 = "1.2.3.4"`,
 		`public-ipv6 = "2001:db8::1"`,
 		"[domain-fronting]",
@@ -144,8 +137,11 @@ func TestRenderConfig(t *testing.T) {
 	if strings.Contains(full, `ip = "127.0.0.1"`) {
 		t.Fatalf("domain-fronting must use host, not the deprecated ip key:\n%s", full)
 	}
+	if strings.Contains(full, "ad-tag =") {
+		t.Fatalf("no global ad-tag must be emitted, tags are per client:\n%s", full)
+	}
 	if strings.Contains(full, `"alice" = "0123456789abcdef0123456789abcdef"`) || strings.Contains(full, `"alice" = ""`) {
-		t.Fatalf("a client without an override must not appear in [secret-ad-tags]:\n%s", full)
+		t.Fatalf("a client without a tag must not appear in [secret-ad-tags]:\n%s", full)
 	}
 	// TOML requires top-level keys before any [section] header, and [secrets]
 	// must be the final section so trailing keys are not swallowed by a table.
@@ -225,7 +221,6 @@ func TestFingerprintSplit(t *testing.T) {
 		"rekey":  func(i *Instance) { i.Secrets = []SecretEntry{{Name: "a", Secret: "ee99"}} },
 		"remove": func(i *Instance) { i.Secrets = nil },
 		"rename": func(i *Instance) { i.Secrets = []SecretEntry{{Name: "a2", Secret: "ee"}} },
-		"adTag":  func(i *Instance) { i.AdTag = "0123456789abcdef0123456789abcdef" },
 		"clientAdTag": func(i *Instance) {
 			i.Secrets = []SecretEntry{{Name: "a", Secret: "ee", AdTag: "0123456789abcdef0123456789abcdef"}}
 		},
@@ -246,7 +241,7 @@ func TestFingerprintSplit(t *testing.T) {
 	t.Run("orderInsensitive", func(t *testing.T) {
 		forward := Instance{Secrets: []SecretEntry{{Name: "alice", Secret: "ee11"}, {Name: "bob", Secret: "ee22"}}}
 		reversed := Instance{Secrets: []SecretEntry{{Name: "bob", Secret: "ee22"}, {Name: "alice", Secret: "ee11"}}}
-		if got, want := forward.secretsFingerprint(), "adtag=|alice=ee11;tag=|bob=ee22;tag="; got != want {
+		if got, want := forward.secretsFingerprint(), "alice=ee11;tag=|bob=ee22;tag="; got != want {
 			t.Fatalf("secrets fingerprint must join sorted pairs: got %q, want %q", got, want)
 		}
 		if forward.secretsFingerprint() != reversed.secretsFingerprint() {

+ 6 - 3
internal/web/service/inbound.go

@@ -532,9 +532,9 @@ func (s *InboundService) normalizeStreamSettings(inbound *model.Inbound) {
 
 // normalizeMtprotoSecret rebuilds every mtproto client's FakeTLS secret so it is
 // always valid before the row is persisted, and drops the vestigial inbound-level
-// secret: MTProto is multi-client, so mtg and every share link read only the
-// per-client secrets. Leaving an inbound-level secret behind is what produced
-// stale links that failed with "incorrect client random".
+// secret and adTag: MTProto is multi-client, so mtg and every share link read
+// only the per-client values. Leaving an inbound-level secret behind is what
+// produced stale links that failed with "incorrect client random".
 func (s *InboundService) normalizeMtprotoSecret(inbound *model.Inbound) {
 	if inbound.Protocol != model.MTProto {
 		return
@@ -542,6 +542,9 @@ func (s *InboundService) normalizeMtprotoSecret(inbound *model.Inbound) {
 	if stripped, ok := model.StripMtprotoInboundSecret(inbound.Settings); ok {
 		inbound.Settings = stripped
 	}
+	if stripped, ok := model.StripMtprotoInboundAdTag(inbound.Settings); ok {
+		inbound.Settings = stripped
+	}
 	if healed, ok := model.HealMtprotoClientSecrets(inbound.Settings); ok {
 		inbound.Settings = healed
 	}

+ 1 - 3
internal/web/translation/ar-EG.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "نطاق FakeTLS الافتراضي المستخدم لإنشاء سر عميل جديد. يمكن لكل عميل استخدام نطاقه الخاص.",
         "mtgThrottleMaxConnections": "الحد الأقصى للاتصالات",
         "mtgThrottleMaxConnectionsHint": "تحديد الاتصالات المتزامنة لجميع المستخدمين بتوزيع عادل. القيمة 0 تعطّل الميزة.",
-        "mtgAdTag": "علامة إعلانية (قناة مموّلة)",
-        "mtgAdTagHint": "علامة اختيارية مكوّنة من 32 حرفًا ست عشريًا يتم الحصول عليها عند تسجيل البروكسي في تلغرام. عند تعيينها، يتم توجيه العملاء عبر البروكسيات الوسيطة في تلغرام وتظهر قناة مموّلة أعلى قائمة محادثاتهم. تتطلب عنوان IP عامًا يمكن الوصول إليه - عيّنه أدناه أو اترك mtg يكتشفه تلقائيًا.",
         "mtgAdTagInvalid": "يجب أن تتكوّن العلامة الإعلانية من 32 حرفًا ست عشريًا بالضبط.",
         "mtgPublicIpv4": "عنوان IPv4 العام",
         "mtgPublicIpv6": "عنوان IPv6 العام",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "سر MTProto",
       "mtprotoSecretHint": "سر FakeTLS الخاص بالعميل. أعد التوليد لتغييره.",
       "mtprotoAdTag": "علامة إعلانية (قناة مموّلة)",
-      "mtprotoAdTagHint": "علامة اختيارية مكوّنة من 32 حرفًا ست عشريًا تُطبَّق على هذا العميل فقط بدلًا من العلامة الإعلانية المشتركة. اتركها فارغة لاستخدام العلامة المشتركة.",
+      "mtprotoAdTagHint": "علامة اختيارية مكوّنة من 32 حرفًا ست عشريًا يتم الحصول عليها عند تسجيل البروكسي في تلغرام. عند تعيينها، يتم توجيه هذا العميل عبر البروكسيات الوسيطة في تلغرام وتظهر قناة مموّلة أعلى قائمة محادثاته.",
       "reverseTag": "وسم عكسي",
       "reverseTagPlaceholder": "Reverse tag اختياري",
       "telegramId": "معرّف مستخدم تلغرام",

+ 1 - 3
internal/web/translation/en-US.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "Default FakeTLS domain used to generate a new client's secret. Each client can front its own domain.",
         "mtgThrottleMaxConnections": "Max connections",
         "mtgThrottleMaxConnectionsHint": "Cap concurrent connections across all users with a fair-share limit. 0 disables throttling.",
-        "mtgAdTag": "Ad-tag (sponsored channel)",
-        "mtgAdTagHint": "Optional 32-character hex tag from Telegram's proxy registration. When set, clients are routed through Telegram middle proxies and a sponsored channel appears at the top of their chat list. Requires a reachable public IP - set one below or let mtg auto-detect it.",
         "mtgAdTagInvalid": "Ad-tag must be exactly 32 hexadecimal characters.",
         "mtgPublicIpv4": "Public IPv4",
         "mtgPublicIpv6": "Public IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "MTProto secret",
       "mtprotoSecretHint": "The client's FakeTLS secret. Regenerate to rotate it.",
       "mtprotoAdTag": "Ad-tag (sponsored channel)",
-      "mtprotoAdTagHint": "Optional 32-character hex tag applied to this client only, overriding the shared ad-tag. Leave empty to use the shared tag.",
+      "mtprotoAdTagHint": "Optional 32-character hex tag from Telegram's proxy registration. When set, this client is routed through Telegram middle proxies and a sponsored channel appears at the top of their chat list.",
       "reverseTag": "Reverse tag",
       "reverseTagPlaceholder": "Optional reverse tag",
       "telegramId": "Telegram user ID",

+ 1 - 3
internal/web/translation/es-ES.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "Dominio FakeTLS predeterminado para generar el secreto de un nuevo cliente. Cada cliente puede usar su propio dominio.",
         "mtgThrottleMaxConnections": "Conexiones máximas",
         "mtgThrottleMaxConnectionsHint": "Limita las conexiones simultáneas de todos los usuarios con reparto equitativo. 0 desactiva el límite.",
-        "mtgAdTag": "Ad-tag (canal patrocinado)",
-        "mtgAdTagHint": "Etiqueta hexadecimal opcional de 32 caracteres del registro de proxy de Telegram. Si se establece, los clientes se enrutan a través de los proxies intermedios de Telegram y aparece un canal patrocinado en la parte superior de su lista de chats. Requiere una IP pública accesible: configúrala abajo o deja que mtg la detecte automáticamente.",
         "mtgAdTagInvalid": "El ad-tag debe tener exactamente 32 caracteres hexadecimales.",
         "mtgPublicIpv4": "IPv4 pública",
         "mtgPublicIpv6": "IPv6 pública",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Secreto MTProto",
       "mtprotoSecretHint": "El secreto FakeTLS del cliente. Vuelve a generarlo para cambiarlo.",
       "mtprotoAdTag": "Ad-tag (canal patrocinado)",
-      "mtprotoAdTagHint": "Etiqueta hexadecimal opcional de 32 caracteres aplicada solo a este cliente, en lugar del ad-tag compartido. Déjala vacía para usar la etiqueta compartida.",
+      "mtprotoAdTagHint": "Etiqueta hexadecimal opcional de 32 caracteres del registro de proxy de Telegram. Si se establece, este cliente se enruta a través de los proxies intermedios de Telegram y aparece un canal patrocinado en la parte superior de su lista de chats.",
       "reverseTag": "Etiqueta inversa",
       "reverseTagPlaceholder": "Reverse tag opcional",
       "telegramId": "ID de usuario de Telegram",

+ 1 - 3
internal/web/translation/fa-IR.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "دامنه پیش‌فرض FakeTLS برای ساخت سکرت کلاینت جدید. هر کلاینت می‌تواند دامنه مخصوص خود را داشته باشد.",
         "mtgThrottleMaxConnections": "حداکثر اتصالات",
         "mtgThrottleMaxConnectionsHint": "محدود کردن اتصالات همزمان همه کاربران با تقسیم منصفانه. مقدار ۰ غیرفعال است.",
-        "mtgAdTag": "برچسب تبلیغاتی (کانال حامی)",
-        "mtgAdTagHint": "برچسب هگزادسیمال اختیاری ۳۲ کاراکتری که هنگام ثبت پروکسی در تلگرام دریافت می‌شود. با تنظیم آن، کاربران از طریق پروکسی‌های میانی تلگرام هدایت می‌شوند و یک کانال حامی در بالای فهرست گفتگوهایشان نمایش داده می‌شود. به یک IP عمومی در دسترس نیاز دارد - آن را در پایین تنظیم کنید یا بگذارید mtg به‌طور خودکار آن را تشخیص دهد.",
         "mtgAdTagInvalid": "برچسب تبلیغاتی باید دقیقاً ۳۲ کاراکتر هگزادسیمال باشد.",
         "mtgPublicIpv4": "IPv4 عمومی",
         "mtgPublicIpv6": "IPv6 عمومی",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "سکرت MTProto",
       "mtprotoSecretHint": "سکرت FakeTLS این کلاینت. برای تعویض، دوباره تولید کنید.",
       "mtprotoAdTag": "برچسب تبلیغاتی (کانال حامی)",
-      "mtprotoAdTagHint": "برچسب هگزادسیمال اختیاری ۳۲ کاراکتری که فقط برای این کلاینت به‌جای برچسب تبلیغاتی مشترک اعمال می‌شود. برای استفاده از برچسب مشترک خالی بگذارید.",
+      "mtprotoAdTagHint": "برچسب هگزادسیمال اختیاری ۳۲ کاراکتری که هنگام ثبت پروکسی در تلگرام دریافت می‌شود. با تنظیم آن، این کلاینت از طریق پروکسی‌های میانی تلگرام هدایت می‌شود و یک کانال حامی در بالای فهرست گفتگوهایش نمایش داده می‌شود.",
       "reverseTag": "تگ معکوس",
       "reverseTagPlaceholder": "Reverse tag اختیاری",
       "telegramId": "شناسه کاربر تلگرام",

+ 1 - 3
internal/web/translation/id-ID.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "Domain FakeTLS default untuk membuat secret klien baru. Setiap klien bisa memakai domainnya sendiri.",
         "mtgThrottleMaxConnections": "Koneksi maksimum",
         "mtgThrottleMaxConnectionsHint": "Batasi koneksi bersamaan semua pengguna dengan pembagian adil. 0 menonaktifkan.",
-        "mtgAdTag": "Ad-tag (kanal bersponsor)",
-        "mtgAdTagHint": "Tag heksadesimal 32 karakter opsional dari pendaftaran proxy Telegram. Jika diatur, klien dirutekan melalui proxy perantara Telegram dan kanal bersponsor muncul di bagian atas daftar obrolan mereka. Memerlukan IP publik yang dapat dijangkau - atur di bawah atau biarkan mtg mendeteksinya secara otomatis.",
         "mtgAdTagInvalid": "Ad-tag harus tepat 32 karakter heksadesimal.",
         "mtgPublicIpv4": "IPv4 Publik",
         "mtgPublicIpv6": "IPv6 Publik",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Secret MTProto",
       "mtprotoSecretHint": "Secret FakeTLS klien. Buat ulang untuk menggantinya.",
       "mtprotoAdTag": "Ad-tag (kanal bersponsor)",
-      "mtprotoAdTagHint": "Tag heksadesimal 32 karakter opsional yang hanya berlaku untuk klien ini, menggantikan ad-tag bersama. Biarkan kosong untuk memakai tag bersama.",
+      "mtprotoAdTagHint": "Tag heksadesimal 32 karakter opsional dari pendaftaran proxy Telegram. Jika diatur, klien ini dirutekan melalui proxy perantara Telegram dan kanal bersponsor muncul di bagian atas daftar obrolannya.",
       "reverseTag": "Reverse tag",
       "reverseTagPlaceholder": "Reverse tag opsional",
       "telegramId": "ID pengguna Telegram",

+ 1 - 3
internal/web/translation/ja-JP.json

@@ -546,8 +546,6 @@
         "mtprotoFakeTlsDomainHint": "新しいクライアントのシークレット生成に使う既定の FakeTLS ドメイン。クライアントごとに別のドメインを使用できます。",
         "mtgThrottleMaxConnections": "最大接続数",
         "mtgThrottleMaxConnectionsHint": "全ユーザーの同時接続数を公平配分で制限します。0 で無効。",
-        "mtgAdTag": "広告タグ(スポンサーチャンネル)",
-        "mtgAdTagHint": "Telegram のプロキシ登録で取得する任意の 32 文字の 16 進数タグ。設定すると、クライアントは Telegram の中間プロキシ経由でルーティングされ、スポンサーチャンネルがチャット一覧の先頭に表示されます。到達可能なパブリック IP が必要です。下で設定するか、mtg に自動検出させてください。",
         "mtgAdTagInvalid": "広告タグは正確に 32 文字の 16 進数である必要があります。",
         "mtgPublicIpv4": "パブリック IPv4",
         "mtgPublicIpv6": "パブリック IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "MTProto シークレット",
       "mtprotoSecretHint": "このクライアントの FakeTLS シークレット。変更するには再生成します。",
       "mtprotoAdTag": "広告タグ(スポンサーチャンネル)",
-      "mtprotoAdTagHint": "このクライアントにのみ適用される任意の 32 文字の 16 進数タグで、共有の広告タグを上書きします。空欄にすると共有タグが使われます。",
+      "mtprotoAdTagHint": "Telegram のプロキシ登録で取得する任意の 32 文字の 16 進数タグ。設定すると、このクライアントは Telegram の中間プロキシ経由でルーティングされ、スポンサーチャンネルがチャット一覧の先頭に表示されます。",
       "reverseTag": "Reverse tag",
       "reverseTagPlaceholder": "任意の Reverse tag",
       "telegramId": "Telegram ユーザー ID",

+ 1 - 3
internal/web/translation/pt-BR.json

@@ -546,8 +546,6 @@
         "mtprotoFakeTlsDomainHint": "Domínio FakeTLS padrão usado para gerar o segredo de um novo cliente. Cada cliente pode usar seu próprio domínio.",
         "mtgThrottleMaxConnections": "Conexões máximas",
         "mtgThrottleMaxConnectionsHint": "Limita conexões simultâneas de todos os usuários com distribuição justa. 0 desativa o limite.",
-        "mtgAdTag": "Ad-tag (canal patrocinado)",
-        "mtgAdTagHint": "Tag hexadecimal opcional de 32 caracteres do registro de proxy do Telegram. Quando definida, os clientes são roteados pelos proxies intermediários do Telegram e um canal patrocinado aparece no topo da lista de conversas. Requer um IP público acessível - defina um abaixo ou deixe o mtg detectá-lo automaticamente.",
         "mtgAdTagInvalid": "A ad-tag deve ter exatamente 32 caracteres hexadecimais.",
         "mtgPublicIpv4": "IPv4 público",
         "mtgPublicIpv6": "IPv6 público",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Segredo MTProto",
       "mtprotoSecretHint": "O segredo FakeTLS do cliente. Gere novamente para trocá-lo.",
       "mtprotoAdTag": "Ad-tag (canal patrocinado)",
-      "mtprotoAdTagHint": "Tag hexadecimal opcional de 32 caracteres aplicada somente a este cliente, substituindo a ad-tag compartilhada. Deixe vazio para usar a tag compartilhada.",
+      "mtprotoAdTagHint": "Tag hexadecimal opcional de 32 caracteres do registro de proxy do Telegram. Quando definida, este cliente é roteado pelos proxies intermediários do Telegram e um canal patrocinado aparece no topo da lista de conversas.",
       "reverseTag": "Tag reversa",
       "reverseTagPlaceholder": "Reverse tag opcional",
       "telegramId": "ID de usuário do Telegram",

+ 1 - 3
internal/web/translation/ru-RU.json

@@ -546,8 +546,6 @@
         "mtprotoFakeTlsDomainHint": "Домен FakeTLS по умолчанию для генерации секрета нового клиента. Каждый клиент может использовать свой домен.",
         "mtgThrottleMaxConnections": "Макс. подключений",
         "mtgThrottleMaxConnectionsHint": "Ограничение одновременных подключений всех пользователей по справедливому распределению. 0 — отключено.",
-        "mtgAdTag": "Рекламный тег (спонсорский канал)",
-        "mtgAdTagHint": "Необязательный шестнадцатеричный тег из 32 символов, получаемый при регистрации прокси в Telegram. Если задан, клиенты маршрутизируются через промежуточные прокси Telegram, и спонсорский канал появляется вверху списка чатов. Требуется доступный публичный IP - укажите его ниже или позвольте mtg определить его автоматически.",
         "mtgAdTagInvalid": "Рекламный тег должен содержать ровно 32 шестнадцатеричных символа.",
         "mtgPublicIpv4": "Публичный IPv4",
         "mtgPublicIpv6": "Публичный IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Секрет MTProto",
       "mtprotoSecretHint": "Секрет FakeTLS клиента. Перегенерируйте, чтобы сменить.",
       "mtprotoAdTag": "Рекламный тег (спонсорский канал)",
-      "mtprotoAdTagHint": "Необязательный шестнадцатеричный тег из 32 символов, применяемый только к этому клиенту вместо общего рекламного тега. Оставьте пустым, чтобы использовать общий тег.",
+      "mtprotoAdTagHint": "Необязательный шестнадцатеричный тег из 32 символов, получаемый при регистрации прокси в Telegram. Если задан, этот клиент маршрутизируется через промежуточные прокси Telegram, и спонсорский канал появляется вверху списка чатов.",
       "reverseTag": "Обратный тег",
       "reverseTagPlaceholder": "Необязательный Reverse tag",
       "telegramId": "ID пользователя Telegram",

+ 1 - 3
internal/web/translation/tr-TR.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "Yeni bir istemcinin sırrını oluştururken kullanılan varsayılan FakeTLS alan adı. Her istemci kendi alan adını kullanabilir.",
         "mtgThrottleMaxConnections": "Maks. bağlantı",
         "mtgThrottleMaxConnectionsHint": "Tüm kullanıcıların eşzamanlı bağlantılarını adil paylaşımla sınırlar. 0 devre dışı bırakır.",
-        "mtgAdTag": "Reklam etiketi (sponsorlu kanal)",
-        "mtgAdTagHint": "Telegram proxy kaydından alınan isteğe bağlı 32 karakterlik onaltılık etiket. Ayarlandığında istemciler Telegram ara proxy'leri üzerinden yönlendirilir ve sohbet listelerinin en üstünde sponsorlu bir kanal görünür. Erişilebilir bir genel IP gerektirir - aşağıdan ayarlayın veya mtg'nin otomatik algılamasına izin verin.",
         "mtgAdTagInvalid": "Reklam etiketi tam olarak 32 onaltılık karakter olmalıdır.",
         "mtgPublicIpv4": "Genel IPv4",
         "mtgPublicIpv6": "Genel IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "MTProto sırrı",
       "mtprotoSecretHint": "İstemcinin FakeTLS sırrı. Değiştirmek için yeniden oluşturun.",
       "mtprotoAdTag": "Reklam etiketi (sponsorlu kanal)",
-      "mtprotoAdTagHint": "Yalnızca bu istemciye uygulanan ve ortak reklam etiketinin yerine geçen isteğe bağlı 32 karakterlik onaltılık etiket. Ortak etiketi kullanmak için boş bırakın.",
+      "mtprotoAdTagHint": "Telegram proxy kaydından alınan isteğe bağlı 32 karakterlik onaltılık etiket. Ayarlandığında bu istemci Telegram ara proxy'leri üzerinden yönlendirilir ve sohbet listesinin en üstünde sponsorlu bir kanal görünür.",
       "reverseTag": "Reverse Tag",
       "reverseTagPlaceholder": "İsteğe Bağlı Reverse Tag",
       "telegramId": "Telegram Kullanıcı ID'si",

+ 1 - 3
internal/web/translation/uk-UA.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "Домен FakeTLS за замовчуванням для генерації секрету нового клієнта. Кожен клієнт може використовувати власний домен.",
         "mtgThrottleMaxConnections": "Макс. з'єднань",
         "mtgThrottleMaxConnectionsHint": "Обмеження одночасних з'єднань усіх користувачів зі справедливим розподілом. 0 — вимкнено.",
-        "mtgAdTag": "Рекламний тег (спонсорський канал)",
-        "mtgAdTagHint": "Необовʼязковий шістнадцятковий тег із 32 символів, який видається під час реєстрації проксі в Telegram. Якщо задано, клієнти маршрутизуються через проміжні проксі Telegram, а спонсорський канал зʼявляється вгорі списку чатів. Потрібна доступна публічна IP-адреса - вкажіть її нижче або дозвольте mtg визначити її автоматично.",
         "mtgAdTagInvalid": "Рекламний тег має містити рівно 32 шістнадцяткові символи.",
         "mtgPublicIpv4": "Публічний IPv4",
         "mtgPublicIpv6": "Публічний IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Секрет MTProto",
       "mtprotoSecretHint": "Секрет FakeTLS клієнта. Згенеруйте заново, щоб змінити.",
       "mtprotoAdTag": "Рекламний тег (спонсорський канал)",
-      "mtprotoAdTagHint": "Необовʼязковий шістнадцятковий тег із 32 символів, що застосовується лише до цього клієнта замість спільного рекламного тега. Залиште порожнім, щоб використовувати спільний тег.",
+      "mtprotoAdTagHint": "Необовʼязковий шістнадцятковий тег із 32 символів, який видається під час реєстрації проксі в Telegram. Якщо задано, цей клієнт маршрутизується через проміжні проксі Telegram, а спонсорський канал зʼявляється вгорі списку чатів.",
       "reverseTag": "Зворотний тег",
       "reverseTagPlaceholder": "Необов'язковий Reverse tag",
       "telegramId": "ID користувача Telegram",

+ 1 - 3
internal/web/translation/vi-VN.json

@@ -546,8 +546,6 @@
         "mtprotoFakeTlsDomainHint": "Tên miền FakeTLS mặc định dùng để tạo secret cho client mới. Mỗi client có thể dùng tên miền riêng.",
         "mtgThrottleMaxConnections": "Số kết nối tối đa",
         "mtgThrottleMaxConnectionsHint": "Giới hạn kết nối đồng thời của tất cả người dùng theo phân bổ công bằng. 0 để tắt.",
-        "mtgAdTag": "Ad-tag (kênh tài trợ)",
-        "mtgAdTagHint": "Thẻ thập lục phân 32 ký tự tùy chọn từ đăng ký proxy của Telegram. Khi được đặt, client sẽ được định tuyến qua các proxy trung gian của Telegram và một kênh tài trợ xuất hiện ở đầu danh sách trò chuyện. Cần một IP công khai có thể truy cập - đặt bên dưới hoặc để mtg tự phát hiện.",
         "mtgAdTagInvalid": "Ad-tag phải có đúng 32 ký tự thập lục phân.",
         "mtgPublicIpv4": "IPv4 công khai",
         "mtgPublicIpv6": "IPv6 công khai",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "Secret MTProto",
       "mtprotoSecretHint": "Secret FakeTLS của client. Tạo lại để thay đổi.",
       "mtprotoAdTag": "Ad-tag (kênh tài trợ)",
-      "mtprotoAdTagHint": "Thẻ thập lục phân 32 ký tự tùy chọn chỉ áp dụng cho client này, thay cho ad-tag dùng chung. Để trống để dùng thẻ chung.",
+      "mtprotoAdTagHint": "Thẻ thập lục phân 32 ký tự tùy chọn từ đăng ký proxy của Telegram. Khi được đặt, client này sẽ được định tuyến qua các proxy trung gian của Telegram và một kênh tài trợ xuất hiện ở đầu danh sách trò chuyện.",
       "reverseTag": "Reverse tag",
       "reverseTagPlaceholder": "Reverse tag tùy chọn",
       "telegramId": "ID người dùng Telegram",

+ 1 - 3
internal/web/translation/zh-CN.json

@@ -545,8 +545,6 @@
         "mtprotoFakeTlsDomainHint": "生成新客户端密钥时使用的默认 FakeTLS 域名。每个客户端可使用各自的域名。",
         "mtgThrottleMaxConnections": "最大连接数",
         "mtgThrottleMaxConnectionsHint": "按公平分配限制所有用户的并发连接数。0 表示不限制。",
-        "mtgAdTag": "广告标签(赞助频道)",
-        "mtgAdTagHint": "可选的 32 位十六进制标签,从 Telegram 代理注册处获取。设置后,客户端将通过 Telegram 中间代理路由,赞助频道会显示在其聊天列表顶部。需要可访问的公网 IP —— 在下方设置,或让 mtg 自动检测。",
         "mtgAdTagInvalid": "广告标签必须为 32 位十六进制字符。",
         "mtgPublicIpv4": "公网 IPv4",
         "mtgPublicIpv6": "公网 IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "MTProto 密钥",
       "mtprotoSecretHint": "该客户端的 FakeTLS 密钥。重新生成即可更换。",
       "mtprotoAdTag": "广告标签(赞助频道)",
-      "mtprotoAdTagHint": "可选的 32 位十六进制标签,仅对该客户端生效,覆盖共享广告标签。留空则使用共享标签。",
+      "mtprotoAdTagHint": "可选的 32 位十六进制标签,从 Telegram 代理注册处获取。设置后,该客户端将通过 Telegram 中间代理路由,赞助频道会显示在其聊天列表顶部。",
       "reverseTag": "反向标签",
       "reverseTagPlaceholder": "可选 Reverse tag",
       "telegramId": "Telegram 用户 ID",

+ 1 - 3
internal/web/translation/zh-TW.json

@@ -525,8 +525,6 @@
         "mtprotoFakeTlsDomainHint": "產生新用戶端金鑰時使用的預設 FakeTLS 網域。每個用戶端可使用各自的網域。",
         "mtgThrottleMaxConnections": "最大連線數",
         "mtgThrottleMaxConnectionsHint": "以公平分配限制所有使用者的並行連線數。0 表示不限制。",
-        "mtgAdTag": "廣告標籤(贊助頻道)",
-        "mtgAdTagHint": "選填的 32 位十六進位標籤,從 Telegram 代理註冊處取得。設定後,用戶端會透過 Telegram 中間代理路由,贊助頻道會顯示在其聊天清單頂端。需要可存取的公網 IP —— 在下方設定,或讓 mtg 自動偵測。",
         "mtgAdTagInvalid": "廣告標籤必須為 32 位十六進位字元。",
         "mtgPublicIpv4": "公網 IPv4",
         "mtgPublicIpv6": "公網 IPv6",
@@ -919,7 +917,7 @@
       "mtprotoSecret": "MTProto 金鑰",
       "mtprotoSecretHint": "該用戶端的 FakeTLS 金鑰。重新產生即可更換。",
       "mtprotoAdTag": "廣告標籤(贊助頻道)",
-      "mtprotoAdTagHint": "選填的 32 位十六進位標籤,僅對該用戶端生效,覆蓋共用廣告標籤。留空則使用共用標籤。",
+      "mtprotoAdTagHint": "選填的 32 位十六進位標籤,從 Telegram 代理註冊處取得。設定後,該用戶端會透過 Telegram 中間代理路由,贊助頻道會顯示在其聊天清單頂端。",
       "reverseTag": "反向標籤",
       "reverseTagPlaceholder": "選用 Reverse tag",
       "telegramId": "Telegram 使用者 ID",