diff --git a/app/dashboard/build/locales/en.json b/app/dashboard/build/locales/en.json
index 09f49343..a81f47b5 100644
--- a/app/dashboard/build/locales/en.json
+++ b/app/dashboard/build/locales/en.json
@@ -105,6 +105,7 @@
"hostsDialog.host.wildcard": "Use * to generate a random string (works for wildcard domain names)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "Enable MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "Fragment pattern",
"hostsDialog.fragment.info": "Correct pattern: length,interval,packets",
diff --git a/app/dashboard/build/locales/fa.json b/app/dashboard/build/locales/fa.json
index 57e79ace..0b0fbd88 100644
--- a/app/dashboard/build/locales/fa.json
+++ b/app/dashboard/build/locales/fa.json
@@ -106,6 +106,7 @@
"hostsDialog.host.wildcard": "از * برای ساخت عبارت تصادفی استفاده کنید (برای نامهای wildcard کار میکند)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "فعالسازی MUX",
+ "hostsDialog.randomUserAgent":"استفاده از User Agent تصادفی",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "الگو فرگمنت",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/build/locales/ru.json b/app/dashboard/build/locales/ru.json
index 7d3409aa..6e52cec1 100644
--- a/app/dashboard/build/locales/ru.json
+++ b/app/dashboard/build/locales/ru.json
@@ -103,6 +103,7 @@
"hostsDialog.host.wildcard": "Используйте *, чтобы сгенерировать случайную строку (работает для wildcard доменов)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "Давать возможность MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "Шаблон фрагмента",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/build/locales/zh.json b/app/dashboard/build/locales/zh.json
index f9817e28..65789ad4 100644
--- a/app/dashboard/build/locales/zh.json
+++ b/app/dashboard/build/locales/zh.json
@@ -97,6 +97,7 @@
"hostsDialog.fingerprint": "指纹",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "使能够 MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "碎片图案",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/public/locales/en.json b/app/dashboard/public/locales/en.json
index 09f49343..a81f47b5 100644
--- a/app/dashboard/public/locales/en.json
+++ b/app/dashboard/public/locales/en.json
@@ -105,6 +105,7 @@
"hostsDialog.host.wildcard": "Use * to generate a random string (works for wildcard domain names)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "Enable MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "Fragment pattern",
"hostsDialog.fragment.info": "Correct pattern: length,interval,packets",
diff --git a/app/dashboard/public/locales/fa.json b/app/dashboard/public/locales/fa.json
index 57e79ace..0b0fbd88 100644
--- a/app/dashboard/public/locales/fa.json
+++ b/app/dashboard/public/locales/fa.json
@@ -106,6 +106,7 @@
"hostsDialog.host.wildcard": "از * برای ساخت عبارت تصادفی استفاده کنید (برای نامهای wildcard کار میکند)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "فعالسازی MUX",
+ "hostsDialog.randomUserAgent":"استفاده از User Agent تصادفی",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "الگو فرگمنت",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/public/locales/ru.json b/app/dashboard/public/locales/ru.json
index 7d3409aa..6e52cec1 100644
--- a/app/dashboard/public/locales/ru.json
+++ b/app/dashboard/public/locales/ru.json
@@ -103,6 +103,7 @@
"hostsDialog.host.wildcard": "Используйте *, чтобы сгенерировать случайную строку (работает для wildcard доменов)",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "Давать возможность MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "Шаблон фрагмента",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/public/locales/zh.json b/app/dashboard/public/locales/zh.json
index f9817e28..65789ad4 100644
--- a/app/dashboard/public/locales/zh.json
+++ b/app/dashboard/public/locales/zh.json
@@ -97,6 +97,7 @@
"hostsDialog.fingerprint": "指纹",
"hostsDialog.sockopt": "Sockopt",
"hostsDialog.muxEnable": "使能够 MUX",
+ "hostsDialog.randomUserAgent":"Use random user agent",
"hostsDialog.allowinsecure": "Allow Insecure",
"hostsDialog.fragment": "碎片图案",
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
diff --git a/app/dashboard/src/components/HostsDialog.tsx b/app/dashboard/src/components/HostsDialog.tsx
index 9eb8efad..89d79d05 100644
--- a/app/dashboard/src/components/HostsDialog.tsx
+++ b/app/dashboard/src/components/HostsDialog.tsx
@@ -119,6 +119,7 @@ const hostsSchema = z.record(
allowinsecure: z.boolean().nullable().default(false),
is_disabled: z.boolean().default(true),
fragment_setting: z.string().nullable(),
+ random_user_agent: z.boolean().default(false),
security: z.string(),
alpn: z.string(),
fingerprint: z.string(),
@@ -175,6 +176,7 @@ const AccordionInbound: FC = ({
allowinsecure: false,
is_disabled: false,
fragment_setting: "",
+ random_user_agent: false,
security: "inbound_default",
alpn: "",
fingerprint: "",
@@ -920,6 +922,28 @@ const AccordionInbound: FC = ({
)}
+
+
+ {t("hostsDialog.randomUserAgent")}
+
+ {accordionErrors &&
+ accordionErrors[index]?.random_user_agent && (
+
+ {accordionErrors[index]?.random_user_agent?.message}
+
+ )}
+
diff --git a/app/db/crud.py b/app/db/crud.py
index cb2edb1d..1affebb4 100644
--- a/app/db/crud.py
+++ b/app/db/crud.py
@@ -85,6 +85,7 @@ def update_hosts(db: Session, inbound_tag: str, modified_hosts: List[ProxyHostMo
is_disabled=host.is_disabled,
mux_enable=host.mux_enable,
fragment_setting=host.fragment_setting,
+ random_user_agent=host.random_user_agent,
) for host in modified_hosts
]
db.commit()
diff --git a/app/db/migrations/versions/31f92220c0d0_add_support_random_user_agent.py b/app/db/migrations/versions/31f92220c0d0_add_support_random_user_agent.py
new file mode 100644
index 00000000..65c9b341
--- /dev/null
+++ b/app/db/migrations/versions/31f92220c0d0_add_support_random_user_agent.py
@@ -0,0 +1,28 @@
+"""Add Support Random User-Agent
+
+Revision ID: 31f92220c0d0
+Revises: 4f045f53bef8
+Create Date: 2024-06-01 21:28:33.310627
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '31f92220c0d0'
+down_revision = '4f045f53bef8'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('hosts', sa.Column('random_user_agent', sa.Boolean(), server_default='0', nullable=False))
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('hosts', 'random_user_agent')
+ # ### end Alembic commands ###
diff --git a/app/db/models.py b/app/db/models.py
index 65d94bf9..d19d90f6 100644
--- a/app/db/models.py
+++ b/app/db/models.py
@@ -194,6 +194,7 @@ class ProxyHost(Base):
is_disabled = Column(Boolean, nullable=True, default=False)
mux_enable = Column(Boolean, nullable=False, default=False, server_default='0')
fragment_setting = Column(String(100), nullable=True)
+ random_user_agent = Column(Boolean, nullable=False, default=False, server_default='0')
class System(Base):
diff --git a/app/models/proxy.py b/app/models/proxy.py
index bef62f36..c83ff9c2 100644
--- a/app/models/proxy.py
+++ b/app/models/proxy.py
@@ -149,6 +149,7 @@ class ProxyHost(BaseModel):
is_disabled: Union[bool, None] = None
mux_enable: Union[bool, None] = None
fragment_setting: Optional[str] = Field(None, nullable=True)
+ random_user_agent: Union[bool, None] = None
class Config:
orm_mode = True
diff --git a/app/subscription/share.py b/app/subscription/share.py
index cd6e3869..4d12de41 100644
--- a/app/subscription/share.py
+++ b/app/subscription/share.py
@@ -282,7 +282,8 @@ def process_inbounds_and_tags(
"ais": host["allowinsecure"]
or inbound.get("allowinsecure", ""),
"mux_enable": host["mux_enable"],
- "fragment_setting": host["fragment_setting"]
+ "fragment_setting": host["fragment_setting"],
+ "random_user_agent": host["random_user_agent"],
}
)
diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py
index 7310baf8..5b5ffe8c 100644
--- a/app/subscription/singbox.py
+++ b/app/subscription/singbox.py
@@ -1,8 +1,9 @@
import json
+from random import choice
from app.templates import render_template
from app.subscription.funcs import get_grpc_gun
-from config import SINGBOX_SUBSCRIPTION_TEMPLATE, MUX_TEMPLATE
+from config import SINGBOX_SUBSCRIPTION_TEMPLATE, MUX_TEMPLATE, USER_AGENT_TEMPLATE
class SingBoxConfiguration(str):
@@ -11,6 +12,13 @@ def __init__(self):
template = render_template(SINGBOX_SUBSCRIPTION_TEMPLATE)
self.config = json.loads(template)
self.mux_template = render_template(MUX_TEMPLATE)
+ temp_user_agent_data = render_template(USER_AGENT_TEMPLATE)
+ user_agent_data = json.loads(temp_user_agent_data)
+
+ if 'list' in user_agent_data and isinstance(user_agent_data['list'], list):
+ self.user_agent_list = user_agent_data['list']
+ else:
+ self.user_agent_list = []
def add_outbound(self, outbound_data):
self.config["outbounds"].append(outbound_data)
@@ -65,8 +73,8 @@ def tls_config(sni=None, fp=None, tls=None, pbk=None,
return config
- @staticmethod
- def transport_config(transport_type='',
+ def transport_config(self,
+ transport_type='',
host='',
path='',
method='',
@@ -74,7 +82,8 @@ def transport_config(transport_type='',
ping_timeout="15s",
max_early_data=None,
early_data_header_name=None,
- permit_without_stream=False):
+ permit_without_stream=False,
+ random_user_agent: bool = False):
transport_config = {}
@@ -87,8 +96,12 @@ def transport_config(transport_type='',
transport_config['path'] = path
if method:
transport_config['method'] = method
+ if host or random_user_agent:
+ transport_config['headers'] = {}
if host:
transport_config["host"] = [host]
+ if random_user_agent:
+ transport_config['headers']['User-Agent'] = choice(self.user_agent_list)
if idle_timeout:
transport_config['idle_timeout'] = idle_timeout
if ping_timeout:
@@ -97,8 +110,12 @@ def transport_config(transport_type='',
elif transport_type == "ws":
if path:
transport_config['path'] = path
+ if host or random_user_agent:
+ transport_config['headers'] = {}
if host:
transport_config['headers'] = {'Host': host}
+ if random_user_agent:
+ transport_config['headers']['User-Agent'] = choice(self.user_agent_list)
if max_early_data is not None:
transport_config['max_early_data'] = max_early_data
if early_data_header_name:
@@ -118,6 +135,9 @@ def transport_config(transport_type='',
transport_config['host'] = host
if path:
transport_config['path'] = path
+ if random_user_agent:
+ transport_config['headers'] = {}
+ transport_config['headers']['User-Agent'] = choice(self.user_agent_list)
return transport_config
@@ -139,6 +159,7 @@ def make_outbound(self,
headers='',
ais='',
mux_enable: bool = False,
+ random_user_agent: bool = False,
):
config = {
@@ -173,7 +194,8 @@ def make_outbound(self,
host=host,
path=path,
max_early_data=max_early_data,
- early_data_header_name=early_data_header_name
+ early_data_header_name=early_data_header_name,
+ random_user_agent=random_user_agent,
)
else:
config["network"] = net
@@ -215,7 +237,8 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
sid=inbound.get('sid', ''),
headers=inbound['header_type'],
ais=inbound.get('ais', ''),
- mux_enable=inbound.get('mux_enable', False))
+ mux_enable=inbound.get('mux_enable', False),
+ random_user_agent=inbound.get('random_user_agent', False),)
if inbound['protocol'] == 'vmess':
outbound['uuid'] = settings['id']
diff --git a/app/subscription/v2ray.py b/app/subscription/v2ray.py
index ca9d900e..625432db 100644
--- a/app/subscription/v2ray.py
+++ b/app/subscription/v2ray.py
@@ -1,5 +1,6 @@
import base64
import json
+from random import choice
import urllib.parse as urlparse
from typing import Union
from uuid import UUID
@@ -8,7 +9,7 @@
from app.subscription.funcs import get_grpc_gun, get_grpc_multi
from app.templates import render_template
-from config import (MUX_TEMPLATE, V2RAY_SUBSCRIPTION_TEMPLATE)
+from config import (MUX_TEMPLATE, V2RAY_SUBSCRIPTION_TEMPLATE, USER_AGENT_TEMPLATE)
class V2rayShareLink(str):
@@ -341,6 +342,13 @@ def __init__(self):
self.config = []
self.template = render_template(V2RAY_SUBSCRIPTION_TEMPLATE)
self.mux_template = render_template(MUX_TEMPLATE)
+ temp_user_agent_data = render_template(USER_AGENT_TEMPLATE)
+ user_agent_data = json.loads(temp_user_agent_data)
+
+ if 'list' in user_agent_data and isinstance(user_agent_data['list'], list):
+ self.user_agent_list = user_agent_data['list']
+ else:
+ self.user_agent_list = []
def add_config(self, remarks, outbounds):
json_template = json.loads(self.template)
@@ -390,8 +398,8 @@ def reality_config(sni=None, fp=None, pbk=None, sid=None, spx=None):
return realitySettings
- @staticmethod
- def ws_config(path=None, host=None):
+
+ def ws_config(self, path=None, host=None, random_user_agent=None):
wsSettings = {}
wsSettings["headers"] = {}
@@ -399,17 +407,21 @@ def ws_config(path=None, host=None):
wsSettings["path"] = path
if host:
wsSettings["headers"]["Host"] = host
+ if random_user_agent:
+ wsSettings["headers"]["User-Agent"] = choice(self.user_agent_list)
return wsSettings
- @staticmethod
- def httpupgrade_config(path=None, host=None):
+ def httpupgrade_config(self, path=None, host=None, random_user_agent=None):
httpupgradeSettings = {}
+ httpupgradeSettings["headers"] = {}
if path:
httpupgradeSettings["path"] = path
if host:
httpupgradeSettings["host"] = host
+ if random_user_agent:
+ httpupgradeSettings["headers"]["User-Agent"] = choice(self.user_agent_list)
return httpupgradeSettings
@@ -427,8 +439,7 @@ def grpc_config(path=None, multiMode=False):
return grpcSettings
- @staticmethod
- def tcp_http_config(path=None, host=None):
+ def tcp_http_config(self, path=None, host=None, random_user_agent=None):
tcpSettings = {}
if any((path, host)):
@@ -440,7 +451,6 @@ def tcp_http_config(path=None, host=None):
tcpSettings["header"]["request"]["headers"] = {}
tcpSettings["header"]["request"]["method"] = "GET"
- tcpSettings["header"]["request"]["headers"]["User-Agent"] = []
tcpSettings["header"]["request"]["headers"]["Accept-Encoding"] = ["gzip, deflate"]
tcpSettings["header"]["request"]["headers"]["Connection"] = ["keep-alive"]
tcpSettings["header"]["request"]["headers"]["Pragma"] = "no-cache"
@@ -451,12 +461,17 @@ def tcp_http_config(path=None, host=None):
if host:
tcpSettings["header"]["request"]["headers"]["Host"] = [host]
+ if random_user_agent:
+ tcpSettings["header"]["request"]["headers"]["User-Agent"] = [choice(self.user_agent_list)]
+ else:
+ tcpSettings["header"]["request"]["headers"]["User-Agent"] = []
+
return tcpSettings
- @staticmethod
- def h2_config(path=None, host=None):
+ def h2_config(self, path=None, host=None, random_user_agent=None):
httpSettings = {}
+ httpSettings["headers"] = {}
if path:
httpSettings["path"] = path
else:
@@ -464,7 +479,9 @@ def h2_config(path=None, host=None):
if host:
httpSettings["host"] = [host]
else:
- httpSettings["host"] = {}
+ httpSettings["host"] = []
+ if random_user_agent:
+ httpSettings["headers"]["User-Agent"] = [choice(self.user_agent_list)]
return httpSettings
@@ -647,24 +664,25 @@ def make_stream_setting(self,
ais='',
dialer_proxy='',
multiMode: bool = False,
+ random_user_agent: bool = False,
):
if net == "ws":
- network_setting = self.ws_config(path=path, host=host)
+ network_setting = self.ws_config(path=path, host=host, random_user_agent=random_user_agent)
elif net == "grpc":
network_setting = self.grpc_config(path=path, multiMode=multiMode)
elif net == "h2":
- network_setting = self.h2_config(path=path, host=host)
+ network_setting = self.h2_config(path=path, host=host, random_user_agent=random_user_agent)
elif net == "kcp":
network_setting = self.kcp_config(
path=path, host=host, header=headers)
elif net == "tcp":
- network_setting = self.tcp_http_config(path=path, host=host)
+ network_setting = self.tcp_http_config(path=path, host=host, random_user_agent=random_user_agent)
elif net == "quic":
network_setting = self.quic_config(
path=path, host=host, header=headers)
elif net == "httpupgrade":
- network_setting = self.httpupgrade_config(path=path, host=host)
+ network_setting = self.httpupgrade_config(path=path, host=host, random_user_agent=random_user_agent)
if tls == "tls":
tls_settings = self.tls_config(sni=sni, fp=fp, alpn=alpn, ais=ais)
@@ -769,6 +787,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
ais=inbound.get('ais', ''),
dialer_proxy=dialer_proxy,
multiMode=multi_mode,
+ random_user_agent=inbound.get('random_user_agent', False),
)
mux_json = json.loads(self.mux_template)
diff --git a/app/templates/user_agent/default.json b/app/templates/user_agent/default.json
new file mode 100644
index 00000000..abb85644
--- /dev/null
+++ b/app/templates/user_agent/default.json
@@ -0,0 +1,104 @@
+{
+ "list":[
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 PageSpeedPlus/1.0.0",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 11.6; rv:92.0) Gecko/20100101 Firefox/92.0",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.4.14 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:126.0) Gecko/20100101 Firefox/126.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/25.0 Chrome/121.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.4.16 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.5.3 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.5.12 Chrome/120.0.6099.283 Electron/28.2.3 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.4.16 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 OPR/109.0.0.0",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.4.13 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.4.13 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1.1 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.5.12 Chrome/120.0.6099.283 Electron/28.2.3 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1.2 Safari/605.1.15",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:124.0) Gecko/20100101 Firefox/124.0",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) obsidian/1.5.3 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/117.0.0.0 Mobile Safari/537.36"
+ ]
+}
\ No newline at end of file
diff --git a/app/xray/__init__.py b/app/xray/__init__.py
index 8f13d942..1bc23fe6 100644
--- a/app/xray/__init__.py
+++ b/app/xray/__init__.py
@@ -61,7 +61,8 @@ def hosts(storage: dict):
else host.security.value,
"allowinsecure": host.allowinsecure,
"mux_enable": host.mux_enable,
- "fragment_setting": host.fragment_setting
+ "fragment_setting": host.fragment_setting,
+ "random_user_agent": host.random_user_agent,
} for host in inbound_hosts if not host.is_disabled
]
diff --git a/config.py b/config.py
index fa22f994..1565bc90 100755
--- a/config.py
+++ b/config.py
@@ -50,6 +50,8 @@
SINGBOX_SUBSCRIPTION_TEMPLATE = config("SINGBOX_SUBSCRIPTION_TEMPLATE", default="singbox/default.json")
MUX_TEMPLATE = config("MUX_TEMPLATE", default="mux/default.json")
V2RAY_SUBSCRIPTION_TEMPLATE = config("V2RAY_SUBSCRIPTION_TEMPLATE", default="v2ray/default.json")
+USER_AGENT_TEMPLATE = config("USER_AGENT_TEMPLATE", default="user_agent/default.json")
+
USE_CUSTOM_JSON_DEFAULT = config("USE_CUSTOM_JSON_DEFAULT", default=False, cast=bool)
USE_CUSTOM_JSON_FOR_V2RAYN = config("USE_CUSTOM_JSON_FOR_V2RAYN", default=False, cast=bool)