gitscope
https://dev.kronoz.win/r/marzneshin.git
app/utils/share.py · v0.7.0
Source
1 a47eb4c Dawsh 2y ago import base64
2 24112b4 Dawsh 2y ago import ipaddress
3 a47eb4c Dawsh 2y ago import json
4 a47eb4c Dawsh 2y ago import random
5 a47eb4c Dawsh 2y ago import secrets
6 19a42f0 Dawsh 2y ago from collections import defaultdict
7 a47eb4c Dawsh 2y ago from datetime import datetime as dt, timedelta
8 406d607 dawsh 2y ago from importlib import resources
9 e422d72 Dawsh 2y ago from typing import Literal, Union, List, Type
10 a47eb4c Dawsh 2y ago from uuid import UUID
11 a47eb4c Dawsh 2y ago
12 a47eb4c Dawsh 2y ago from jdatetime import date as jd
13 f1ab16c Dawsh 2y ago from v2share import (
14 f1ab16c Dawsh 2y ago V2Data,
15 f1ab16c Dawsh 2y ago SingBoxConfig,
16 f1ab16c Dawsh 2y ago ClashConfig,
17 f1ab16c Dawsh 2y ago ClashMetaConfig,
18 f1ab16c Dawsh 2y ago XrayConfig,
19 0f3112a Dawsh 2y ago WireGuardConfig,
20 f1ab16c Dawsh 2y ago )
21 e422d72 Dawsh 2y ago from v2share.base import BaseConfig
22 4056c66 Dawsh 1y ago from v2share.data import MuxCoolSettings as V2MuxCoolSettings
23 4056c66 Dawsh 1y ago from v2share.data import MuxSettings as V2MuxSettings
24 4056c66 Dawsh 1y ago from v2share.data import SingBoxMuxSettings as V2SingBoxMuxSettings
25 4056c66 Dawsh 1y ago from v2share.data import SplitHttpSettings as V2SplitHttpSettings
26 4056c66 Dawsh 1y ago from v2share.data import XMuxSettings as V2XMuxSettings
27 4056c66 Dawsh 1y ago from v2share.data import XrayNoise
28 d6600c5 Dawsh 2y ago from v2share.links import LinksConfig
29 a47eb4c Dawsh 2y ago
30 8111eb7 Dawsh 2y ago from app.config.env import (
31 8111eb7 Dawsh 2y ago XRAY_SUBSCRIPTION_TEMPLATE,
32 8111eb7 Dawsh 2y ago SINGBOX_SUBSCRIPTION_TEMPLATE,
33 8111eb7 Dawsh 2y ago CLASH_SUBSCRIPTION_TEMPLATE,
34 fb4b69d Dawsh 2y ago SUBSCRIPTION_PAGE_TEMPLATE,
35 8111eb7 Dawsh 2y ago )
36 4056c66 Dawsh 1y ago from app.db import GetDB
37 4056c66 Dawsh 1y ago from app.db.crud import get_hosts_for_user
38 01cdf2e Dawsh 2y ago from app.models.proxy import (
39 01cdf2e Dawsh 2y ago InboundHostSecurity,
40 4056c66 Dawsh 1y ago SplitHttpSettings,
41 4056c66 Dawsh 1y ago MuxSettings,
42 01cdf2e Dawsh 2y ago )
43 fb4b69d Dawsh 2y ago from app.models.settings import SubscriptionSettings
44 d377c4c Dawsh 2y ago from app.models.user import UserResponse, UserExpireStrategy
45 fb4b69d Dawsh 2y ago from app.templates import render_template
46 24112b4 Dawsh 2y ago from app.utils.keygen import gen_uuid, gen_password, generate_curve25519_pbk
47 a47eb4c Dawsh 2y ago from app.utils.system import get_public_ip, readable_size
48 a47eb4c Dawsh 2y ago
49 a47eb4c Dawsh 2y ago SERVER_IP = get_public_ip()
50 a47eb4c Dawsh 2y ago
51 fb4b69d Dawsh 2y ago ACTIVITY_EMOJIS = {
52 fb4b69d Dawsh 2y ago True: "✅",
53 fb4b69d Dawsh 2y ago False: "❌",
54 a47eb4c Dawsh 2y ago }
55 a47eb4c Dawsh 2y ago
56 e422d72 Dawsh 2y ago subscription_handlers: dict[str, Type[BaseConfig]] = {
57 0178858 Dawsh 2y ago "links": LinksConfig,
58 d6600c5 Dawsh 2y ago "xray": XrayConfig,
59 d6600c5 Dawsh 2y ago "clash-meta": ClashMetaConfig,
60 d6600c5 Dawsh 2y ago "clash": ClashConfig,
61 d6600c5 Dawsh 2y ago "sing-box": SingBoxConfig,
62 0f3112a Dawsh 2y ago "wireguard": WireGuardConfig,
63 d6600c5 Dawsh 2y ago }
64 a47eb4c Dawsh 2y ago
65 406d607 dawsh 2y ago handlers_templates = {
66 406d607 dawsh 2y ago LinksConfig: None,
67 0f3112a Dawsh 2y ago WireGuardConfig: None,
68 406d607 dawsh 2y ago XrayConfig: XRAY_SUBSCRIPTION_TEMPLATE
69 406d607 dawsh 2y ago or resources.files("app.templates") / "xray.json",
70 406d607 dawsh 2y ago ClashConfig: CLASH_SUBSCRIPTION_TEMPLATE,
71 406d607 dawsh 2y ago ClashMetaConfig: CLASH_SUBSCRIPTION_TEMPLATE,
72 406d607 dawsh 2y ago SingBoxConfig: SINGBOX_SUBSCRIPTION_TEMPLATE
73 406d607 dawsh 2y ago or resources.files("app.templates") / "sing-box.json",
74 406d607 dawsh 2y ago }
75 406d607 dawsh 2y ago
76 a47eb4c Dawsh 2y ago
77 fb4b69d Dawsh 2y ago def generate_subscription_template(
78 fb4b69d Dawsh 2y ago db_user, subscription_settings: SubscriptionSettings
79 fb4b69d Dawsh 2y ago ):
80 fb4b69d Dawsh 2y ago links = generate_subscription(
81 fb4b69d Dawsh 2y ago user=db_user,
82 fb4b69d Dawsh 2y ago config_format="links",
83 fb4b69d Dawsh 2y ago use_placeholder=not db_user.is_active
84 fb4b69d Dawsh 2y ago and subscription_settings.placeholder_if_disabled,
85 fb4b69d Dawsh 2y ago placeholder_remark=subscription_settings.placeholder_remark,
86 fb4b69d Dawsh 2y ago shuffle=subscription_settings.shuffle_configs,
87 fb4b69d Dawsh 2y ago ).split()
88 fb4b69d Dawsh 2y ago return render_template(
89 fb4b69d Dawsh 2y ago SUBSCRIPTION_PAGE_TEMPLATE,
90 fb4b69d Dawsh 2y ago {"user": UserResponse.model_validate(db_user), "links": links},
91 fb4b69d Dawsh 2y ago )
92 fb4b69d Dawsh 2y ago
93 fb4b69d Dawsh 2y ago
94 a47eb4c Dawsh 2y ago def generate_subscription(
95 a47eb4c Dawsh 2y ago user: "UserResponse",
96 0178858 Dawsh 2y ago config_format: Literal["links", "xray", "clash-meta", "clash", "sing-box"],
97 d6600c5 Dawsh 2y ago as_base64: bool = False,
98 fb4b69d Dawsh 2y ago use_placeholder: bool = False,
99 fb4b69d Dawsh 2y ago placeholder_remark: str = "disabled",
100 fb4b69d Dawsh 2y ago shuffle: bool = False,
101 a47eb4c Dawsh 2y ago ) -> str:
102 db68a58 Dawsh 2y ago extra_data = UserResponse.model_validate(user).model_dump(
103 db68a58 Dawsh 2y ago exclude={"subscription_url", "services", "inbounds"}
104 db68a58 Dawsh 2y ago )
105 d6600c5 Dawsh 2y ago format_variables = setup_format_variables(extra_data)
106 d6600c5 Dawsh 2y ago
107 d6600c5 Dawsh 2y ago if config_format not in subscription_handlers.keys():
108 a47eb4c Dawsh 2y ago raise ValueError(f'Unsupported format "{config_format}"')
109 a47eb4c Dawsh 2y ago
110 406d607 dawsh 2y ago subscription_handler_class = subscription_handlers[config_format]
111 406d607 dawsh 2y ago if template_path := handlers_templates[subscription_handler_class]:
112 f1ab16c Dawsh 2y ago subscription_handler = subscription_handler_class(
113 f1ab16c Dawsh 2y ago template_path=template_path
114 f1ab16c Dawsh 2y ago )
115 406d607 dawsh 2y ago else:
116 406d607 dawsh 2y ago subscription_handler = subscription_handler_class()
117 406d607 dawsh 2y ago
118 fb4b69d Dawsh 2y ago if use_placeholder:
119 fb4b69d Dawsh 2y ago placeholder_config = V2Data(
120 fb4b69d Dawsh 2y ago "vmess",
121 fb4b69d Dawsh 2y ago placeholder_remark.format_map(format_variables),
122 fb4b69d Dawsh 2y ago "127.0.0.1",
123 fb4b69d Dawsh 2y ago 80,
124 fb4b69d Dawsh 2y ago )
125 fb4b69d Dawsh 2y ago configs = [placeholder_config]
126 fb4b69d Dawsh 2y ago
127 fb4b69d Dawsh 2y ago else:
128 fb4b69d Dawsh 2y ago configs = generate_user_configs(
129 fb4b69d Dawsh 2y ago user.inbounds,
130 fb4b69d Dawsh 2y ago user.key,
131 24112b4 Dawsh 2y ago user.id,
132 fb4b69d Dawsh 2y ago format_variables,
133 4056c66 Dawsh 1y ago chaining_support=subscription_handler.chaining_support,
134 fb4b69d Dawsh 2y ago )
135 fb4b69d Dawsh 2y ago
136 fb4b69d Dawsh 2y ago subscription_handler.add_proxies(configs)
137 e422d72 Dawsh 2y ago config = subscription_handler.render(sort=True, shuffle=shuffle)
138 a47eb4c Dawsh 2y ago
139 f1ab16c Dawsh 2y ago return (
140 f1ab16c Dawsh 2y ago config if not as_base64 else base64.b64encode(config.encode()).decode()
141 f1ab16c Dawsh 2y ago )
142 a47eb4c Dawsh 2y ago
143 a47eb4c Dawsh 2y ago
144 a47eb4c Dawsh 2y ago def format_time_left(seconds_left: int) -> str:
145 a47eb4c Dawsh 2y ago if not seconds_left or seconds_left <= 0:
146 a47eb4c Dawsh 2y ago return "∞"
147 a47eb4c Dawsh 2y ago
148 a47eb4c Dawsh 2y ago minutes, seconds = divmod(seconds_left, 60)
149 a47eb4c Dawsh 2y ago hours, minutes = divmod(minutes, 60)
150 a47eb4c Dawsh 2y ago days, hours = divmod(hours, 24)
151 a47eb4c Dawsh 2y ago months, days = divmod(days, 30)
152 a47eb4c Dawsh 2y ago
153 a47eb4c Dawsh 2y ago result = []
154 a47eb4c Dawsh 2y ago if months:
155 a47eb4c Dawsh 2y ago result.append(f"{months}m")
156 a47eb4c Dawsh 2y ago if days:
157 a47eb4c Dawsh 2y ago result.append(f"{days}d")
158 a47eb4c Dawsh 2y ago if hours and (days < 7):
159 a47eb4c Dawsh 2y ago result.append(f"{hours}h")
160 a47eb4c Dawsh 2y ago if minutes and not (months or days):
161 a47eb4c Dawsh 2y ago result.append(f"{minutes}m")
162 a47eb4c Dawsh 2y ago if seconds and not (months or days):
163 a47eb4c Dawsh 2y ago result.append(f"{seconds}s")
164 a47eb4c Dawsh 2y ago return " ".join(result)
165 a47eb4c Dawsh 2y ago
166 01cdf2e Dawsh 2y ago
167 0f3112a Dawsh 2y ago def calculate_client_address(interface_address: str, user_id: int) -> str:
168 0f3112a Dawsh 2y ago try:
169 0f3112a Dawsh 2y ago interface = ipaddress.ip_interface(interface_address)
170 0f3112a Dawsh 2y ago address = interface.ip
171 0f3112a Dawsh 2y ago network = interface.network
172 0f3112a Dawsh 2y ago except ValueError:
173 0f3112a Dawsh 2y ago return ""
174 0f3112a Dawsh 2y ago user_address = network[user_id]
175 0f3112a Dawsh 2y ago if user_address == network[0]:
176 0f3112a Dawsh 2y ago user_address = network.broadcast_address - 1
177 0f3112a Dawsh 2y ago if user_address == address:
178 0f3112a Dawsh 2y ago user_address = network.broadcast_address - 2
179 0f3112a Dawsh 2y ago return user_address.compressed + "/" + str(user_address.max_prefixlen)
180 0f3112a Dawsh 2y ago
181 0f3112a Dawsh 2y ago
182 a47eb4c Dawsh 2y ago def setup_format_variables(extra_data: dict) -> dict:
183 d377c4c Dawsh 2y ago expire_strategy = extra_data.get("expire_strategy")
184 d377c4c Dawsh 2y ago expire_datetime = extra_data.get("expire_date")
185 d377c4c Dawsh 2y ago usage_duration = extra_data.get("usage_duration")
186 a47eb4c Dawsh 2y ago
187 d377c4c Dawsh 2y ago if expire_strategy != UserExpireStrategy.START_ON_FIRST_USE:
188 f446990 Dawsh 2y ago if expire_datetime is not None:
189 f446990 Dawsh 2y ago seconds_left = (expire_datetime - dt.utcnow()).total_seconds()
190 a47eb4c Dawsh 2y ago expire_date = expire_datetime.date()
191 a47eb4c Dawsh 2y ago jalali_expire_date = jd.fromgregorian(
192 f1ab16c Dawsh 2y ago year=expire_date.year,
193 f1ab16c Dawsh 2y ago month=expire_date.month,
194 f1ab16c Dawsh 2y ago day=expire_date.day,
195 a47eb4c Dawsh 2y ago ).strftime("%Y-%m-%d")
196 a47eb4c Dawsh 2y ago days_left = (expire_datetime - dt.utcnow()).days + 1
197 a47eb4c Dawsh 2y ago time_left = format_time_left(seconds_left)
198 a47eb4c Dawsh 2y ago else:
199 d377c4c Dawsh 2y ago days_left, time_left, expire_date, jalali_expire_date = "∞" * 4
200 a47eb4c Dawsh 2y ago else:
201 d377c4c Dawsh 2y ago if usage_duration is not None and usage_duration >= 0:
202 d377c4c Dawsh 2y ago days_left = timedelta(seconds=usage_duration).days
203 d377c4c Dawsh 2y ago time_left = format_time_left(usage_duration)
204 a47eb4c Dawsh 2y ago expire_date = "-"
205 a47eb4c Dawsh 2y ago jalali_expire_date = "-"
206 a47eb4c Dawsh 2y ago else:
207 d377c4c Dawsh 2y ago days_left, time_left, expire_date, jalali_expire_date = "∞" * 4
208 a47eb4c Dawsh 2y ago
209 a47eb4c Dawsh 2y ago if extra_data.get("data_limit"):
210 a47eb4c Dawsh 2y ago data_limit = readable_size(extra_data["data_limit"])
211 a47eb4c Dawsh 2y ago data_left = extra_data["data_limit"] - extra_data["used_traffic"]
212 a47eb4c Dawsh 2y ago if data_left < 0:
213 a47eb4c Dawsh 2y ago data_left = 0
214 a47eb4c Dawsh 2y ago data_left = readable_size(data_left)
215 a47eb4c Dawsh 2y ago else:
216 fb4b69d Dawsh 2y ago data_limit, data_left = "∞" * 2
217 a47eb4c Dawsh 2y ago
218 fb4b69d Dawsh 2y ago status_emoji = ACTIVITY_EMOJIS.get(extra_data.get("is_active")) or ""
219 a47eb4c Dawsh 2y ago
220 19a42f0 Dawsh 2y ago format_variables = defaultdict(
221 19a42f0 Dawsh 2y ago lambda: "<missing>",
222 19a42f0 Dawsh 2y ago {
223 19a42f0 Dawsh 2y ago "SERVER_IP": SERVER_IP,
224 19a42f0 Dawsh 2y ago "USERNAME": extra_data.get("username", "{USERNAME}"),
225 19a42f0 Dawsh 2y ago "DATA_USAGE": readable_size(extra_data.get("used_traffic")),
226 19a42f0 Dawsh 2y ago "DATA_LIMIT": data_limit,
227 19a42f0 Dawsh 2y ago "DATA_LEFT": data_left,
228 19a42f0 Dawsh 2y ago "DAYS_LEFT": days_left,
229 19a42f0 Dawsh 2y ago "EXPIRE_DATE": expire_date,
230 19a42f0 Dawsh 2y ago "JALALI_EXPIRE_DATE": jalali_expire_date,
231 19a42f0 Dawsh 2y ago "TIME_LEFT": time_left,
232 19a42f0 Dawsh 2y ago "STATUS_EMOJI": status_emoji,
233 19a42f0 Dawsh 2y ago },
234 19a42f0 Dawsh 2y ago )
235 a47eb4c Dawsh 2y ago
236 a47eb4c Dawsh 2y ago return format_variables
237 a47eb4c Dawsh 2y ago
238 a47eb4c Dawsh 2y ago
239 fb4b69d Dawsh 2y ago def generate_user_configs(
240 a47eb4c Dawsh 2y ago inbounds: list,
241 d6600c5 Dawsh 2y ago key: str,
242 24112b4 Dawsh 2y ago user_id: int,
243 a47eb4c Dawsh 2y ago format_variables: dict,
244 4056c66 Dawsh 1y ago chaining_support: bool,
245 a47eb4c Dawsh 2y ago ) -> Union[List, str]:
246 a47eb4c Dawsh 2y ago salt = secrets.token_hex(8)
247 fb4b69d Dawsh 2y ago configs = []
248 01cdf2e Dawsh 2y ago
249 4056c66 Dawsh 1y ago with GetDB() as db:
250 4056c66 Dawsh 1y ago hosts = get_hosts_for_user(db, user_id)
251 a47eb4c Dawsh 2y ago
252 4056c66 Dawsh 1y ago for host in hosts:
253 4056c66 Dawsh 1y ago chained_hosts = [c.chained_host for c in host.chain]
254 4056c66 Dawsh 1y ago if chained_hosts and not chaining_support:
255 a47eb4c Dawsh 2y ago continue
256 4056c66 Dawsh 1y ago data = create_config(
257 4056c66 Dawsh 1y ago host, key, format_variables, salt, user_id, chained_hosts
258 4056c66 Dawsh 1y ago )
259 4056c66 Dawsh 1y ago configs.append(data)
260 4056c66 Dawsh 1y ago
261 4056c66 Dawsh 1y ago return configs
262 4056c66 Dawsh 1y ago
263 4056c66 Dawsh 1y ago
264 4056c66 Dawsh 1y ago def create_config(
265 4056c66 Dawsh 1y ago host, key, format_variables, salt, user_id, next_hosts: list | None = None
266 4056c66 Dawsh 1y ago ):
267 4056c66 Dawsh 1y ago if next_hosts is None:
268 4056c66 Dawsh 1y ago next_hosts = []
269 a47eb4c Dawsh 2y ago
270 4056c66 Dawsh 1y ago if host.inbound:
271 4056c66 Dawsh 1y ago inbound, protocol = (
272 4056c66 Dawsh 1y ago json.loads(host.inbound.config),
273 4056c66 Dawsh 1y ago host.inbound.protocol,
274 dd32f70 Dawsh 2y ago )
275 4056c66 Dawsh 1y ago network = inbound.get("network")
276 4056c66 Dawsh 1y ago auth_uuid, auth_password = UUID(gen_uuid(key)), gen_password(key)
277 4056c66 Dawsh 1y ago else:
278 4056c66 Dawsh 1y ago inbound, protocol, network = {}, host.host_protocol, host.host_network
279 4056c66 Dawsh 1y ago auth_uuid, auth_password = (
280 4056c66 Dawsh 1y ago UUID(host.uuid) if host.uuid else None
281 4056c66 Dawsh 1y ago ), host.password
282 4056c66 Dawsh 1y ago
283 4056c66 Dawsh 1y ago format_variables.update(
284 4056c66 Dawsh 1y ago {
285 4056c66 Dawsh 1y ago "PROTOCOL": (
286 4056c66 Dawsh 1y ago host.inbound.protocol.value
287 4056c66 Dawsh 1y ago if host.inbound
288 4056c66 Dawsh 1y ago else host.host_protocol
289 01cdf2e Dawsh 2y ago )
290 4056c66 Dawsh 1y ago }
291 4056c66 Dawsh 1y ago )
292 4056c66 Dawsh 1y ago format_variables.update({"TRANSPORT": network or "<missing>"})
293 4056c66 Dawsh 1y ago
294 4056c66 Dawsh 1y ago host_snis = host.sni.split(",") if host.sni else []
295 4056c66 Dawsh 1y ago sni_list = host_snis or inbound.get("sni", [])
296 4056c66 Dawsh 1y ago if sni_list:
297 4056c66 Dawsh 1y ago sni = random.choice(sni_list).replace("*", salt)
298 4056c66 Dawsh 1y ago else:
299 4056c66 Dawsh 1y ago sni = ""
300 4056c66 Dawsh 1y ago
301 4056c66 Dawsh 1y ago host_hosts = host.host.split(",") if host.host else []
302 4056c66 Dawsh 1y ago req_host_list = host_hosts or inbound.get("host", [])
303 4056c66 Dawsh 1y ago if req_host_list:
304 4056c66 Dawsh 1y ago req_host = random.choice(req_host_list).replace("*", salt)
305 4056c66 Dawsh 1y ago else:
306 4056c66 Dawsh 1y ago req_host = ""
307 4056c66 Dawsh 1y ago
308 4056c66 Dawsh 1y ago host_tls = (
309 4056c66 Dawsh 1y ago None
310 4056c66 Dawsh 1y ago if host.security == InboundHostSecurity.inbound_default
311 4056c66 Dawsh 1y ago else host.security.value
312 4056c66 Dawsh 1y ago )
313 4056c66 Dawsh 1y ago splithttp_settings = (
314 4056c66 Dawsh 1y ago SplitHttpSettings.model_validate(host.splithttp_settings)
315 4056c66 Dawsh 1y ago if host.splithttp_settings
316 4056c66 Dawsh 1y ago else None
317 4056c66 Dawsh 1y ago )
318 4056c66 Dawsh 1y ago mux_settings = (
319 4056c66 Dawsh 1y ago MuxSettings.model_validate(host.mux_settings)
320 4056c66 Dawsh 1y ago if host.mux_settings
321 4056c66 Dawsh 1y ago else None
322 4056c66 Dawsh 1y ago )
323 4056c66 Dawsh 1y ago
324 4056c66 Dawsh 1y ago data = V2Data(
325 4056c66 Dawsh 1y ago host.inbound.protocol.value if host.inbound else host.host_protocol,
326 4056c66 Dawsh 1y ago host.remark.format_map(format_variables),
327 4056c66 Dawsh 1y ago host.address.format_map(format_variables),
328 4056c66 Dawsh 1y ago host.port or inbound.get("port", 0),
329 4056c66 Dawsh 1y ago transport_type=network,
330 4056c66 Dawsh 1y ago sni=sni,
331 4056c66 Dawsh 1y ago host=req_host,
332 4056c66 Dawsh 1y ago tls=host_tls or inbound.get("tls"),
333 4056c66 Dawsh 1y ago header_type=host.header_type or inbound.get("header_type"),
334 4056c66 Dawsh 1y ago alpn=host.alpn if host.alpn != "none" else None,
335 4056c66 Dawsh 1y ago path=(
336 4056c66 Dawsh 1y ago host.path.format_map(format_variables)
337 4056c66 Dawsh 1y ago if host.path
338 4056c66 Dawsh 1y ago else inbound.get("path")
339 4056c66 Dawsh 1y ago ),
340 4056c66 Dawsh 1y ago fingerprint=host.fingerprint.value or inbound.get("fp"),
341 4056c66 Dawsh 1y ago reality_pbk=inbound.get("pbk"),
342 4056c66 Dawsh 1y ago reality_sid=inbound.get("sid"),
343 4056c66 Dawsh 1y ago client_address=calculate_client_address(
344 4056c66 Dawsh 1y ago inbound.get("address"), user_id
345 4056c66 Dawsh 1y ago ),
346 4056c66 Dawsh 1y ago flow=host.flow or inbound.get("flow"),
347 4056c66 Dawsh 1y ago dns_servers=(host.dns_servers.split(",") if host.dns_servers else []),
348 4056c66 Dawsh 1y ago mtu=host.mtu,
349 4056c66 Dawsh 1y ago allowed_ips=(
350 4056c66 Dawsh 1y ago list(map(str.strip, host.allowed_ips.split(",")))
351 4056c66 Dawsh 1y ago if host.allowed_ips
352 4056c66 Dawsh 1y ago else None
353 4056c66 Dawsh 1y ago ),
354 4056c66 Dawsh 1y ago allow_insecure=host.allowinsecure,
355 4056c66 Dawsh 1y ago uuid=auth_uuid,
356 4056c66 Dawsh 1y ago password=auth_password,
357 4056c66 Dawsh 1y ago ed25519=generate_curve25519_pbk(key),
358 4056c66 Dawsh 1y ago early_data=host.early_data,
359 4056c66 Dawsh 1y ago splithttp_settings=(
360 4056c66 Dawsh 1y ago V2SplitHttpSettings(
361 4056c66 Dawsh 1y ago mode=splithttp_settings.mode,
362 4056c66 Dawsh 1y ago no_grpc_header=splithttp_settings.no_grpc_header,
363 4056c66 Dawsh 1y ago padding_bytes=splithttp_settings.padding_bytes,
364 4056c66 Dawsh 1y ago xmux=(
365 4056c66 Dawsh 1y ago V2XMuxSettings(**splithttp_settings.xmux.model_dump())
366 4056c66 Dawsh 1y ago if splithttp_settings.xmux
367 4056c66 Dawsh 1y ago else None
368 24112b4 Dawsh 2y ago ),
369 4056c66 Dawsh 1y ago )
370 4056c66 Dawsh 1y ago if splithttp_settings
371 4056c66 Dawsh 1y ago else None
372 4056c66 Dawsh 1y ago ),
373 4056c66 Dawsh 1y ago mux_settings=(
374 4056c66 Dawsh 1y ago V2MuxSettings(
375 4056c66 Dawsh 1y ago protocol=mux_settings.protocol,
376 4056c66 Dawsh 1y ago sing_box_mux_settings=(
377 4056c66 Dawsh 1y ago V2SingBoxMuxSettings(
378 4056c66 Dawsh 1y ago **mux_settings.sing_box_mux_settings.model_dump()
379 4056c66 Dawsh 1y ago )
380 4056c66 Dawsh 1y ago if mux_settings.sing_box_mux_settings
381 4056c66 Dawsh 1y ago else None
382 d636f3f Dawsh 2y ago ),
383 4056c66 Dawsh 1y ago mux_cool_settings=(
384 4056c66 Dawsh 1y ago V2MuxCoolSettings(
385 4056c66 Dawsh 1y ago **mux_settings.mux_cool_settings.model_dump()
386 4056c66 Dawsh 1y ago )
387 4056c66 Dawsh 1y ago if mux_settings.mux_cool_settings
388 b584c6c Dawsh 2y ago else None
389 b584c6c Dawsh 2y ago ),
390 a47eb4c Dawsh 2y ago )
391 4056c66 Dawsh 1y ago if mux_settings
392 4056c66 Dawsh 1y ago else None
393 4056c66 Dawsh 1y ago ),
394 4056c66 Dawsh 1y ago http_headers=host.http_headers,
395 4056c66 Dawsh 1y ago shadowsocks_method=host.shadowsocks_method or "chacha20-ietf-poly1305",
396 4056c66 Dawsh 1y ago shadowtls_version=host.shadowtls_version
397 4056c66 Dawsh 1y ago or inbound.get("shadowtls_version"),
398 4056c66 Dawsh 1y ago weight=host.weight,
399 4056c66 Dawsh 1y ago xray_noises=(
400 4056c66 Dawsh 1y ago [XrayNoise(**noise) for noise in host.udp_noises]
401 4056c66 Dawsh 1y ago if host.udp_noises
402 4056c66 Dawsh 1y ago else None
403 4056c66 Dawsh 1y ago ),
404 4056c66 Dawsh 1y ago next=(
405 4056c66 Dawsh 1y ago create_config(
406 4056c66 Dawsh 1y ago next_hosts[0],
407 4056c66 Dawsh 1y ago key,
408 4056c66 Dawsh 1y ago format_variables,
409 4056c66 Dawsh 1y ago salt,
410 4056c66 Dawsh 1y ago user_id,
411 4056c66 Dawsh 1y ago next_hosts[1:],
412 4056c66 Dawsh 1y ago )
413 4056c66 Dawsh 1y ago if next_hosts
414 4056c66 Dawsh 1y ago else None
415 4056c66 Dawsh 1y ago ),
416 4056c66 Dawsh 1y ago )
417 4056c66 Dawsh 1y ago if host.fragment:
418 4056c66 Dawsh 1y ago data.fragment = True
419 4056c66 Dawsh 1y ago data.fragment_packets = host.fragment["packets"]
420 4056c66 Dawsh 1y ago data.fragment_length = host.fragment["length"]
421 4056c66 Dawsh 1y ago data.fragment_interval = host.fragment["interval"]
422 fb4b69d Dawsh 2y ago
423 4056c66 Dawsh 1y ago return data
424 a47eb4c Dawsh 2y ago
425 a47eb4c Dawsh 2y ago
426 a47eb4c Dawsh 2y ago def encode_title(text: str) -> str:
427 a47eb4c Dawsh 2y ago return f"base64:{base64.b64encode(text.encode()).decode()}"