gitscope
https://dev.kronoz.win/r/marzneshin.git
app/utils/share.py · v0.1.2
Source
1 a47eb4c Dawsh 2y ago import base64
2 a47eb4c Dawsh 2y ago import json
3 a47eb4c Dawsh 2y ago import random
4 a47eb4c Dawsh 2y ago import secrets
5 a47eb4c Dawsh 2y ago import urllib.parse as urlparse
6 a47eb4c Dawsh 2y ago from datetime import datetime as dt, timedelta
7 a47eb4c Dawsh 2y ago from typing import TYPE_CHECKING, Literal, Union, List
8 a47eb4c Dawsh 2y ago from uuid import UUID
9 a47eb4c Dawsh 2y ago
10 a47eb4c Dawsh 2y ago import yaml
11 a47eb4c Dawsh 2y ago from jdatetime import date as jd
12 a47eb4c Dawsh 2y ago
13 a47eb4c Dawsh 2y ago from app.models.proxy import FormatVariables, InboundBase, InboundHost, InboundHostSecurity
14 a47eb4c Dawsh 2y ago from app.templates import render_template
15 a47eb4c Dawsh 2y ago from app.utils.system import get_public_ip, readable_size
16 a47eb4c Dawsh 2y ago from app.utils.keygen import generate_settings
17 a47eb4c Dawsh 2y ago from app.db import GetDB, crud
18 a47eb4c Dawsh 2y ago
19 a47eb4c Dawsh 2y ago if TYPE_CHECKING:
20 a47eb4c Dawsh 2y ago from app.models.user import UserResponse
21 a47eb4c Dawsh 2y ago
22 a47eb4c Dawsh 2y ago from config import CLASH_SUBSCRIPTION_TEMPLATE, SINGBOX_SUBSCRIPTION_TEMPLATE
23 a47eb4c Dawsh 2y ago
24 a47eb4c Dawsh 2y ago SERVER_IP = get_public_ip()
25 a47eb4c Dawsh 2y ago
26 a47eb4c Dawsh 2y ago STATUS_EMOJIS = {
27 a47eb4c Dawsh 2y ago "active": "✅",
28 a47eb4c Dawsh 2y ago "expired": "⌛️",
29 a47eb4c Dawsh 2y ago "limited": "🪫",
30 a47eb4c Dawsh 2y ago "disabled": "❌",
31 a47eb4c Dawsh 2y ago "connect_to_start": "🔌",
32 a47eb4c Dawsh 2y ago }
33 a47eb4c Dawsh 2y ago
34 a47eb4c Dawsh 2y ago class V2rayShareLink(str):
35 a47eb4c Dawsh 2y ago @classmethod
36 a47eb4c Dawsh 2y ago def vmess(
37 a47eb4c Dawsh 2y ago cls,
38 a47eb4c Dawsh 2y ago remark: str,
39 a47eb4c Dawsh 2y ago address: str,
40 a47eb4c Dawsh 2y ago port: int,
41 a47eb4c Dawsh 2y ago id: Union[str, UUID],
42 a47eb4c Dawsh 2y ago host="",
43 a47eb4c Dawsh 2y ago net="tcp",
44 a47eb4c Dawsh 2y ago path="",
45 a47eb4c Dawsh 2y ago type="",
46 a47eb4c Dawsh 2y ago tls="none",
47 a47eb4c Dawsh 2y ago sni="",
48 a47eb4c Dawsh 2y ago fp="",
49 a47eb4c Dawsh 2y ago alpn="",
50 a47eb4c Dawsh 2y ago pbk="",
51 a47eb4c Dawsh 2y ago sid="",
52 a47eb4c Dawsh 2y ago spx="",
53 a47eb4c Dawsh 2y ago ais="",
54 a47eb4c Dawsh 2y ago ):
55 a47eb4c Dawsh 2y ago payload = {
56 a47eb4c Dawsh 2y ago "add": address,
57 a47eb4c Dawsh 2y ago "aid": "0",
58 a47eb4c Dawsh 2y ago "host": host,
59 a47eb4c Dawsh 2y ago "id": str(id),
60 a47eb4c Dawsh 2y ago "net": net,
61 a47eb4c Dawsh 2y ago "path": path,
62 a47eb4c Dawsh 2y ago "port": port,
63 a47eb4c Dawsh 2y ago "ps": remark,
64 a47eb4c Dawsh 2y ago "scy": "auto",
65 a47eb4c Dawsh 2y ago "tls": tls,
66 a47eb4c Dawsh 2y ago "type": type,
67 a47eb4c Dawsh 2y ago "v": "2",
68 a47eb4c Dawsh 2y ago }
69 a47eb4c Dawsh 2y ago
70 a47eb4c Dawsh 2y ago if tls == "tls":
71 a47eb4c Dawsh 2y ago payload["sni"] = sni
72 a47eb4c Dawsh 2y ago payload["fp"] = fp
73 a47eb4c Dawsh 2y ago payload["alpn"] = alpn
74 a47eb4c Dawsh 2y ago if ais:
75 a47eb4c Dawsh 2y ago payload["allowInsecure"] = 1
76 a47eb4c Dawsh 2y ago elif tls == "reality":
77 a47eb4c Dawsh 2y ago payload["sni"] = sni
78 a47eb4c Dawsh 2y ago payload["fp"] = fp
79 a47eb4c Dawsh 2y ago payload["pbk"] = pbk
80 a47eb4c Dawsh 2y ago payload["sid"] = sid
81 a47eb4c Dawsh 2y ago payload["spx"] = spx
82 a47eb4c Dawsh 2y ago
83 a47eb4c Dawsh 2y ago return (
84 a47eb4c Dawsh 2y ago "vmess://"
85 a47eb4c Dawsh 2y ago + base64.b64encode(
86 a47eb4c Dawsh 2y ago json.dumps(payload, sort_keys=True).encode("utf-8")
87 a47eb4c Dawsh 2y ago ).decode()
88 a47eb4c Dawsh 2y ago )
89 a47eb4c Dawsh 2y ago
90 a47eb4c Dawsh 2y ago @classmethod
91 a47eb4c Dawsh 2y ago def vless(
92 a47eb4c Dawsh 2y ago cls,
93 a47eb4c Dawsh 2y ago remark: str,
94 a47eb4c Dawsh 2y ago address: str,
95 a47eb4c Dawsh 2y ago port: int,
96 a47eb4c Dawsh 2y ago id: Union[str, UUID],
97 a47eb4c Dawsh 2y ago net="ws",
98 a47eb4c Dawsh 2y ago path="",
99 a47eb4c Dawsh 2y ago host="",
100 a47eb4c Dawsh 2y ago type="",
101 a47eb4c Dawsh 2y ago flow="",
102 a47eb4c Dawsh 2y ago tls="none",
103 a47eb4c Dawsh 2y ago sni="",
104 a47eb4c Dawsh 2y ago fp="",
105 a47eb4c Dawsh 2y ago alpn="",
106 a47eb4c Dawsh 2y ago pbk="",
107 a47eb4c Dawsh 2y ago sid="",
108 a47eb4c Dawsh 2y ago spx="",
109 a47eb4c Dawsh 2y ago ais="",
110 a47eb4c Dawsh 2y ago ):
111 a47eb4c Dawsh 2y ago payload = {"security": tls, "type": net,
112 a47eb4c Dawsh 2y ago "host": host, "headerType": type}
113 a47eb4c Dawsh 2y ago if flow and (net in ("tcp", "kcp") and type != "http"):
114 a47eb4c Dawsh 2y ago payload["flow"] = flow
115 a47eb4c Dawsh 2y ago
116 a47eb4c Dawsh 2y ago if net == "grpc":
117 a47eb4c Dawsh 2y ago payload["serviceName"] = path
118 a47eb4c Dawsh 2y ago else:
119 a47eb4c Dawsh 2y ago payload["path"] = path
120 a47eb4c Dawsh 2y ago
121 a47eb4c Dawsh 2y ago if tls == "tls":
122 a47eb4c Dawsh 2y ago payload["sni"] = sni
123 a47eb4c Dawsh 2y ago payload["fp"] = fp
124 a47eb4c Dawsh 2y ago payload["alpn"] = alpn
125 a47eb4c Dawsh 2y ago if ais:
126 a47eb4c Dawsh 2y ago payload["allowInsecure"] = 1
127 a47eb4c Dawsh 2y ago elif tls == "reality":
128 a47eb4c Dawsh 2y ago payload["sni"] = sni
129 a47eb4c Dawsh 2y ago payload["fp"] = fp
130 a47eb4c Dawsh 2y ago payload["pbk"] = pbk
131 a47eb4c Dawsh 2y ago payload["sid"] = sid
132 a47eb4c Dawsh 2y ago payload["spx"] = spx
133 a47eb4c Dawsh 2y ago
134 a47eb4c Dawsh 2y ago return (
135 a47eb4c Dawsh 2y ago "vless://"
136 a47eb4c Dawsh 2y ago + f"{id}@{address}:{port}?"
137 a47eb4c Dawsh 2y ago + urlparse.urlencode(payload)
138 a47eb4c Dawsh 2y ago + f"#{( urlparse.quote(remark))}"
139 a47eb4c Dawsh 2y ago )
140 a47eb4c Dawsh 2y ago
141 a47eb4c Dawsh 2y ago @classmethod
142 a47eb4c Dawsh 2y ago def trojan(
143 a47eb4c Dawsh 2y ago cls,
144 a47eb4c Dawsh 2y ago remark: str,
145 a47eb4c Dawsh 2y ago address: str,
146 a47eb4c Dawsh 2y ago port: int,
147 a47eb4c Dawsh 2y ago password: str,
148 a47eb4c Dawsh 2y ago net="tcp",
149 a47eb4c Dawsh 2y ago path="",
150 a47eb4c Dawsh 2y ago host="",
151 a47eb4c Dawsh 2y ago type="",
152 a47eb4c Dawsh 2y ago flow="",
153 a47eb4c Dawsh 2y ago tls="none",
154 a47eb4c Dawsh 2y ago sni="",
155 a47eb4c Dawsh 2y ago fp="",
156 a47eb4c Dawsh 2y ago alpn="",
157 a47eb4c Dawsh 2y ago pbk="",
158 a47eb4c Dawsh 2y ago sid="",
159 a47eb4c Dawsh 2y ago spx="",
160 a47eb4c Dawsh 2y ago ais="",
161 a47eb4c Dawsh 2y ago ):
162 a47eb4c Dawsh 2y ago payload = {"security": tls, "type": net,
163 a47eb4c Dawsh 2y ago "host": host, "headerType": type}
164 a47eb4c Dawsh 2y ago if flow and (net in ("tcp", "kcp") and type != "http"):
165 a47eb4c Dawsh 2y ago payload["flow"] = flow
166 a47eb4c Dawsh 2y ago
167 a47eb4c Dawsh 2y ago if net == "grpc":
168 a47eb4c Dawsh 2y ago payload["serviceName"] = path
169 a47eb4c Dawsh 2y ago else:
170 a47eb4c Dawsh 2y ago payload["path"] = path
171 a47eb4c Dawsh 2y ago
172 a47eb4c Dawsh 2y ago if tls == "tls":
173 a47eb4c Dawsh 2y ago payload["sni"] = sni
174 a47eb4c Dawsh 2y ago payload["fp"] = fp
175 a47eb4c Dawsh 2y ago payload["alpn"] = alpn
176 a47eb4c Dawsh 2y ago if ais:
177 a47eb4c Dawsh 2y ago payload["allowInsecure"] = 1
178 a47eb4c Dawsh 2y ago elif tls == "reality":
179 a47eb4c Dawsh 2y ago payload["sni"] = sni
180 a47eb4c Dawsh 2y ago payload["fp"] = fp
181 a47eb4c Dawsh 2y ago payload["pbk"] = pbk
182 a47eb4c Dawsh 2y ago payload["sid"] = sid
183 a47eb4c Dawsh 2y ago payload["spx"] = spx
184 a47eb4c Dawsh 2y ago
185 a47eb4c Dawsh 2y ago return (
186 a47eb4c Dawsh 2y ago "trojan://"
187 a47eb4c Dawsh 2y ago + f"{urlparse.quote(password, safe=':')}@{address}:{port}?"
188 a47eb4c Dawsh 2y ago + urlparse.urlencode(payload)
189 a47eb4c Dawsh 2y ago + f"#{urlparse.quote(remark)}"
190 a47eb4c Dawsh 2y ago )
191 a47eb4c Dawsh 2y ago
192 a47eb4c Dawsh 2y ago @classmethod
193 a47eb4c Dawsh 2y ago def shadowsocks(
194 a47eb4c Dawsh 2y ago cls, remark: str, address: str, port: int, password: str, method: str
195 a47eb4c Dawsh 2y ago ):
196 a47eb4c Dawsh 2y ago return (
197 a47eb4c Dawsh 2y ago "ss://"
198 a47eb4c Dawsh 2y ago + base64.b64encode(f"{method}:{password}".encode()).decode()
199 a47eb4c Dawsh 2y ago + f"@{address}:{port}#{urlparse.quote(remark)}"
200 a47eb4c Dawsh 2y ago )
201 a47eb4c Dawsh 2y ago
202 a47eb4c Dawsh 2y ago
203 a47eb4c Dawsh 2y ago class ClashConfiguration(object):
204 a47eb4c Dawsh 2y ago def __init__(self):
205 a47eb4c Dawsh 2y ago self.data = {
206 a47eb4c Dawsh 2y ago "proxies": [],
207 a47eb4c Dawsh 2y ago "proxy-groups": [],
208 a47eb4c Dawsh 2y ago # Some clients rely on "rules" option and will fail without it.
209 a47eb4c Dawsh 2y ago "rules": [],
210 a47eb4c Dawsh 2y ago }
211 a47eb4c Dawsh 2y ago self.proxy_remarks = []
212 a47eb4c Dawsh 2y ago
213 a47eb4c Dawsh 2y ago def render(self):
214 a47eb4c Dawsh 2y ago return yaml.dump(
215 a47eb4c Dawsh 2y ago yaml.load(
216 a47eb4c Dawsh 2y ago render_template(
217 a47eb4c Dawsh 2y ago CLASH_SUBSCRIPTION_TEMPLATE,
218 a47eb4c Dawsh 2y ago {"conf": self.data, "proxy_remarks": self.proxy_remarks},
219 a47eb4c Dawsh 2y ago ),
220 a47eb4c Dawsh 2y ago Loader=yaml.SafeLoader,
221 a47eb4c Dawsh 2y ago ),
222 a47eb4c Dawsh 2y ago sort_keys=False,
223 a47eb4c Dawsh 2y ago allow_unicode=True,
224 a47eb4c Dawsh 2y ago )
225 a47eb4c Dawsh 2y ago
226 a47eb4c Dawsh 2y ago def __str__(self) -> str:
227 a47eb4c Dawsh 2y ago return self.render()
228 a47eb4c Dawsh 2y ago
229 a47eb4c Dawsh 2y ago def __repr__(self) -> str:
230 a47eb4c Dawsh 2y ago return self.render()
231 a47eb4c Dawsh 2y ago
232 a47eb4c Dawsh 2y ago def _remark_validation(self, remark):
233 a47eb4c Dawsh 2y ago if not remark in self.proxy_remarks:
234 a47eb4c Dawsh 2y ago return remark
235 a47eb4c Dawsh 2y ago c = 2
236 a47eb4c Dawsh 2y ago while True:
237 a47eb4c Dawsh 2y ago new = f"{remark} ({c})"
238 a47eb4c Dawsh 2y ago if not new in self.proxy_remarks:
239 a47eb4c Dawsh 2y ago return new
240 a47eb4c Dawsh 2y ago c += 1
241 a47eb4c Dawsh 2y ago
242 a47eb4c Dawsh 2y ago def make_node(
243 a47eb4c Dawsh 2y ago self,
244 a47eb4c Dawsh 2y ago name: str,
245 a47eb4c Dawsh 2y ago type: str,
246 a47eb4c Dawsh 2y ago server: str,
247 a47eb4c Dawsh 2y ago port: int,
248 a47eb4c Dawsh 2y ago network: str,
249 a47eb4c Dawsh 2y ago tls: bool,
250 a47eb4c Dawsh 2y ago sni: str,
251 a47eb4c Dawsh 2y ago host: str,
252 a47eb4c Dawsh 2y ago path: str,
253 a47eb4c Dawsh 2y ago udp: bool = True,
254 a47eb4c Dawsh 2y ago alpn: str = "",
255 a47eb4c Dawsh 2y ago ais: bool = "",
256 a47eb4c Dawsh 2y ago ):
257 a47eb4c Dawsh 2y ago if type == "shadowsocks":
258 a47eb4c Dawsh 2y ago type = "ss"
259 a47eb4c Dawsh 2y ago
260 a47eb4c Dawsh 2y ago remark = self._remark_validation(name)
261 a47eb4c Dawsh 2y ago node = {
262 a47eb4c Dawsh 2y ago "name": remark,
263 a47eb4c Dawsh 2y ago "type": type,
264 a47eb4c Dawsh 2y ago "server": server,
265 a47eb4c Dawsh 2y ago "port": port,
266 a47eb4c Dawsh 2y ago "network": network,
267 a47eb4c Dawsh 2y ago f"{network}-opts": {},
268 a47eb4c Dawsh 2y ago "udp": udp,
269 a47eb4c Dawsh 2y ago }
270 a47eb4c Dawsh 2y ago
271 a47eb4c Dawsh 2y ago if type == "ss": # shadowsocks
272 a47eb4c Dawsh 2y ago return node
273 a47eb4c Dawsh 2y ago
274 a47eb4c Dawsh 2y ago if tls:
275 a47eb4c Dawsh 2y ago node["tls"] = True
276 a47eb4c Dawsh 2y ago if type == "trojan":
277 a47eb4c Dawsh 2y ago node["sni"] = sni
278 a47eb4c Dawsh 2y ago else:
279 a47eb4c Dawsh 2y ago node["servername"] = sni
280 a47eb4c Dawsh 2y ago if alpn:
281 a47eb4c Dawsh 2y ago node["alpn"] = alpn.split(",")
282 a47eb4c Dawsh 2y ago if ais:
283 a47eb4c Dawsh 2y ago node["skip-cert-verify"] = ais
284 a47eb4c Dawsh 2y ago
285 a47eb4c Dawsh 2y ago net_opts = node[f"{network}-opts"]
286 a47eb4c Dawsh 2y ago
287 a47eb4c Dawsh 2y ago if network == "ws":
288 a47eb4c Dawsh 2y ago if path:
289 a47eb4c Dawsh 2y ago net_opts["path"] = path
290 a47eb4c Dawsh 2y ago if host:
291 a47eb4c Dawsh 2y ago net_opts["headers"] = {"Host": host}
292 a47eb4c Dawsh 2y ago
293 a47eb4c Dawsh 2y ago if network == "grpc":
294 a47eb4c Dawsh 2y ago if path:
295 a47eb4c Dawsh 2y ago net_opts["grpc-service-name"] = path
296 a47eb4c Dawsh 2y ago
297 a47eb4c Dawsh 2y ago if network == "h2":
298 a47eb4c Dawsh 2y ago if path:
299 a47eb4c Dawsh 2y ago net_opts["path"] = path
300 a47eb4c Dawsh 2y ago if host:
301 a47eb4c Dawsh 2y ago net_opts["host"] = [host]
302 a47eb4c Dawsh 2y ago
303 a47eb4c Dawsh 2y ago if network == "http" or network == "tcp":
304 a47eb4c Dawsh 2y ago if path:
305 a47eb4c Dawsh 2y ago net_opts["method"] = "GET"
306 a47eb4c Dawsh 2y ago net_opts["path"] = [path]
307 a47eb4c Dawsh 2y ago if host:
308 a47eb4c Dawsh 2y ago net_opts["method"] = "GET"
309 a47eb4c Dawsh 2y ago net_opts["headers"] = {"Host": host}
310 a47eb4c Dawsh 2y ago
311 a47eb4c Dawsh 2y ago return node
312 a47eb4c Dawsh 2y ago
313 a47eb4c Dawsh 2y ago def add(self, remark: str, address: str, inbound: dict, settings: dict):
314 a47eb4c Dawsh 2y ago node = self.make_node(
315 a47eb4c Dawsh 2y ago name=remark,
316 a47eb4c Dawsh 2y ago type=inbound["protocol"],
317 a47eb4c Dawsh 2y ago server=address,
318 a47eb4c Dawsh 2y ago port=inbound["port"],
319 a47eb4c Dawsh 2y ago network=inbound["network"],
320 a47eb4c Dawsh 2y ago tls=(inbound["tls"] == "tls"),
321 a47eb4c Dawsh 2y ago sni=inbound["sni"],
322 a47eb4c Dawsh 2y ago host=inbound["host"],
323 a47eb4c Dawsh 2y ago path=inbound["path"],
324 a47eb4c Dawsh 2y ago udp=True,
325 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
326 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
327 a47eb4c Dawsh 2y ago )
328 a47eb4c Dawsh 2y ago
329 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vmess":
330 a47eb4c Dawsh 2y ago node["uuid"] = settings["id"]
331 a47eb4c Dawsh 2y ago node["alterId"] = 0
332 a47eb4c Dawsh 2y ago node["cipher"] = "auto"
333 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
334 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
335 a47eb4c Dawsh 2y ago
336 a47eb4c Dawsh 2y ago if inbound["protocol"] == "trojan":
337 a47eb4c Dawsh 2y ago node["password"] = settings["password"]
338 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
339 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
340 a47eb4c Dawsh 2y ago
341 a47eb4c Dawsh 2y ago if inbound["protocol"] == "shadowsocks":
342 a47eb4c Dawsh 2y ago node["password"] = settings["password"]
343 a47eb4c Dawsh 2y ago node["cipher"] = settings["method"]
344 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
345 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
346 a47eb4c Dawsh 2y ago
347 a47eb4c Dawsh 2y ago
348 a47eb4c Dawsh 2y ago class ClashMetaConfiguration(ClashConfiguration):
349 a47eb4c Dawsh 2y ago def make_node(
350 a47eb4c Dawsh 2y ago self,
351 a47eb4c Dawsh 2y ago name: str,
352 a47eb4c Dawsh 2y ago type: str,
353 a47eb4c Dawsh 2y ago server: str,
354 a47eb4c Dawsh 2y ago port: int,
355 a47eb4c Dawsh 2y ago network: str,
356 a47eb4c Dawsh 2y ago tls: bool,
357 a47eb4c Dawsh 2y ago sni: str,
358 a47eb4c Dawsh 2y ago host: str,
359 a47eb4c Dawsh 2y ago path: str,
360 a47eb4c Dawsh 2y ago udp: bool = True,
361 a47eb4c Dawsh 2y ago alpn: str = "",
362 a47eb4c Dawsh 2y ago fp: str = "",
363 a47eb4c Dawsh 2y ago pbk: str = "",
364 a47eb4c Dawsh 2y ago sid: str = "",
365 a47eb4c Dawsh 2y ago ais: bool = "",
366 a47eb4c Dawsh 2y ago ):
367 a47eb4c Dawsh 2y ago node = super().make_node(
368 a47eb4c Dawsh 2y ago name=name,
369 a47eb4c Dawsh 2y ago type=type,
370 a47eb4c Dawsh 2y ago server=server,
371 a47eb4c Dawsh 2y ago port=port,
372 a47eb4c Dawsh 2y ago network=network,
373 a47eb4c Dawsh 2y ago tls=tls,
374 a47eb4c Dawsh 2y ago sni=sni,
375 a47eb4c Dawsh 2y ago host=host,
376 a47eb4c Dawsh 2y ago path=path,
377 a47eb4c Dawsh 2y ago #headers="",
378 a47eb4c Dawsh 2y ago udp=udp,
379 a47eb4c Dawsh 2y ago alpn=alpn,
380 a47eb4c Dawsh 2y ago ais=ais,
381 a47eb4c Dawsh 2y ago )
382 a47eb4c Dawsh 2y ago if fp:
383 a47eb4c Dawsh 2y ago node["client-fingerprint"] = fp
384 a47eb4c Dawsh 2y ago if pbk:
385 a47eb4c Dawsh 2y ago node["reality-opts"] = {"public-key": pbk, "short-id": sid}
386 a47eb4c Dawsh 2y ago
387 a47eb4c Dawsh 2y ago return node
388 a47eb4c Dawsh 2y ago
389 a47eb4c Dawsh 2y ago def add(self, remark: str, address: str, inbound: dict, settings: dict):
390 a47eb4c Dawsh 2y ago node = self.make_node(
391 a47eb4c Dawsh 2y ago name=remark,
392 a47eb4c Dawsh 2y ago type=inbound["protocol"],
393 a47eb4c Dawsh 2y ago server=address,
394 a47eb4c Dawsh 2y ago port=inbound["port"],
395 a47eb4c Dawsh 2y ago network=inbound["network"],
396 a47eb4c Dawsh 2y ago tls=(inbound["tls"] in ("tls", "reality")),
397 a47eb4c Dawsh 2y ago sni=inbound["sni"],
398 a47eb4c Dawsh 2y ago host=inbound["host"],
399 a47eb4c Dawsh 2y ago path=inbound["path"],
400 a47eb4c Dawsh 2y ago udp=True,
401 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
402 a47eb4c Dawsh 2y ago fp=inbound.get("fp", ""),
403 a47eb4c Dawsh 2y ago pbk=inbound.get("pbk", ""),
404 a47eb4c Dawsh 2y ago sid=inbound.get("sid", ""),
405 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
406 a47eb4c Dawsh 2y ago )
407 a47eb4c Dawsh 2y ago
408 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vmess":
409 a47eb4c Dawsh 2y ago node["uuid"] = settings["id"]
410 a47eb4c Dawsh 2y ago node["alterId"] = 0
411 a47eb4c Dawsh 2y ago node["cipher"] = "auto"
412 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
413 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
414 a47eb4c Dawsh 2y ago
415 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vless":
416 a47eb4c Dawsh 2y ago node["uuid"] = settings["id"]
417 a47eb4c Dawsh 2y ago
418 a47eb4c Dawsh 2y ago if (
419 a47eb4c Dawsh 2y ago inbound["network"] in ("tcp", "kcp")
420 a47eb4c Dawsh 2y ago and inbound["header_type"] != "http"
421 a47eb4c Dawsh 2y ago ):
422 a47eb4c Dawsh 2y ago node["flow"] = settings.get("flow", "")
423 a47eb4c Dawsh 2y ago
424 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
425 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
426 a47eb4c Dawsh 2y ago
427 a47eb4c Dawsh 2y ago if inbound["protocol"] == "trojan":
428 a47eb4c Dawsh 2y ago node["password"] = settings["password"]
429 a47eb4c Dawsh 2y ago
430 a47eb4c Dawsh 2y ago if (
431 a47eb4c Dawsh 2y ago inbound["network"] in ("tcp", "kcp")
432 a47eb4c Dawsh 2y ago and inbound["header_type"] != "http"
433 a47eb4c Dawsh 2y ago ):
434 a47eb4c Dawsh 2y ago node["flow"] = settings.get("flow", "")
435 a47eb4c Dawsh 2y ago
436 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
437 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
438 a47eb4c Dawsh 2y ago
439 a47eb4c Dawsh 2y ago if inbound["protocol"] == "shadowsocks":
440 a47eb4c Dawsh 2y ago node["password"] = settings["password"]
441 a47eb4c Dawsh 2y ago node["cipher"] = settings["method"]
442 a47eb4c Dawsh 2y ago self.data["proxies"].append(node)
443 a47eb4c Dawsh 2y ago self.proxy_remarks.append(remark)
444 a47eb4c Dawsh 2y ago
445 a47eb4c Dawsh 2y ago
446 a47eb4c Dawsh 2y ago def get_v2ray_link(remark: str, address: str, inbound: dict, settings: dict):
447 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vmess":
448 a47eb4c Dawsh 2y ago return V2rayShareLink.vmess(
449 a47eb4c Dawsh 2y ago remark=remark,
450 a47eb4c Dawsh 2y ago address=address,
451 a47eb4c Dawsh 2y ago port=inbound["port"],
452 a47eb4c Dawsh 2y ago id=settings["id"],
453 a47eb4c Dawsh 2y ago net=inbound["network"],
454 a47eb4c Dawsh 2y ago tls=inbound["tls"],
455 a47eb4c Dawsh 2y ago sni=inbound.get("sni", ""),
456 a47eb4c Dawsh 2y ago fp=inbound.get("fp", ""),
457 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
458 a47eb4c Dawsh 2y ago pbk=inbound.get("pbk", ""),
459 a47eb4c Dawsh 2y ago sid=inbound.get("sid", ""),
460 a47eb4c Dawsh 2y ago spx=inbound.get("spx", ""),
461 a47eb4c Dawsh 2y ago host=inbound["host"],
462 a47eb4c Dawsh 2y ago path=inbound["path"],
463 a47eb4c Dawsh 2y ago type=inbound["header_type"],
464 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
465 a47eb4c Dawsh 2y ago )
466 a47eb4c Dawsh 2y ago
467 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vless":
468 a47eb4c Dawsh 2y ago return V2rayShareLink.vless(
469 a47eb4c Dawsh 2y ago remark=remark,
470 a47eb4c Dawsh 2y ago address=address,
471 a47eb4c Dawsh 2y ago port=inbound["port"],
472 a47eb4c Dawsh 2y ago id=settings["id"],
473 a47eb4c Dawsh 2y ago flow=settings.get("flow", ""),
474 a47eb4c Dawsh 2y ago net=inbound["network"],
475 a47eb4c Dawsh 2y ago tls=inbound["tls"],
476 a47eb4c Dawsh 2y ago sni=inbound.get("sni", ""),
477 a47eb4c Dawsh 2y ago fp=inbound.get("fp", ""),
478 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
479 a47eb4c Dawsh 2y ago pbk=inbound.get("pbk", ""),
480 a47eb4c Dawsh 2y ago sid=inbound.get("sid", ""),
481 a47eb4c Dawsh 2y ago spx=inbound.get("spx", ""),
482 a47eb4c Dawsh 2y ago host=inbound["host"],
483 a47eb4c Dawsh 2y ago path=inbound["path"],
484 a47eb4c Dawsh 2y ago type=inbound["header_type"],
485 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
486 a47eb4c Dawsh 2y ago )
487 a47eb4c Dawsh 2y ago
488 a47eb4c Dawsh 2y ago if inbound["protocol"] == "trojan":
489 a47eb4c Dawsh 2y ago return V2rayShareLink.trojan(
490 a47eb4c Dawsh 2y ago remark=remark,
491 a47eb4c Dawsh 2y ago address=address,
492 a47eb4c Dawsh 2y ago port=inbound["port"],
493 a47eb4c Dawsh 2y ago password=settings["password"],
494 a47eb4c Dawsh 2y ago flow=settings.get("flow", ""),
495 a47eb4c Dawsh 2y ago net=inbound["network"],
496 a47eb4c Dawsh 2y ago tls=inbound["tls"],
497 a47eb4c Dawsh 2y ago sni=inbound.get("sni", ""),
498 a47eb4c Dawsh 2y ago fp=inbound.get("fp", ""),
499 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
500 a47eb4c Dawsh 2y ago pbk=inbound.get("pbk", ""),
501 a47eb4c Dawsh 2y ago sid=inbound.get("sid", ""),
502 a47eb4c Dawsh 2y ago spx=inbound.get("spx", ""),
503 a47eb4c Dawsh 2y ago host=inbound["host"],
504 a47eb4c Dawsh 2y ago path=inbound["path"],
505 a47eb4c Dawsh 2y ago type=inbound["header_type"],
506 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
507 a47eb4c Dawsh 2y ago )
508 a47eb4c Dawsh 2y ago
509 a47eb4c Dawsh 2y ago if inbound["protocol"] == "shadowsocks":
510 a47eb4c Dawsh 2y ago return V2rayShareLink.shadowsocks(
511 a47eb4c Dawsh 2y ago remark=remark,
512 a47eb4c Dawsh 2y ago address=address,
513 a47eb4c Dawsh 2y ago port=inbound["port"],
514 a47eb4c Dawsh 2y ago password=settings["password"],
515 a47eb4c Dawsh 2y ago method=settings["method"],
516 a47eb4c Dawsh 2y ago )
517 a47eb4c Dawsh 2y ago
518 a47eb4c Dawsh 2y ago
519 a47eb4c Dawsh 2y ago class OutlineConfiguration:
520 a47eb4c Dawsh 2y ago def __init__(self):
521 a47eb4c Dawsh 2y ago self.config = {}
522 a47eb4c Dawsh 2y ago
523 a47eb4c Dawsh 2y ago def add_directly(self, data: dict):
524 a47eb4c Dawsh 2y ago self.config.update(data)
525 a47eb4c Dawsh 2y ago
526 a47eb4c Dawsh 2y ago def render(self):
527 a47eb4c Dawsh 2y ago return json.dumps(self.config, indent=0)
528 a47eb4c Dawsh 2y ago
529 a47eb4c Dawsh 2y ago def make_outbound(
530 a47eb4c Dawsh 2y ago self, remark: str, address: str, port: int, password: str, method: str
531 a47eb4c Dawsh 2y ago ):
532 a47eb4c Dawsh 2y ago config = {
533 a47eb4c Dawsh 2y ago "method": method,
534 a47eb4c Dawsh 2y ago "password": password,
535 a47eb4c Dawsh 2y ago "server": address,
536 a47eb4c Dawsh 2y ago "server_port": port,
537 a47eb4c Dawsh 2y ago "tag": remark,
538 a47eb4c Dawsh 2y ago }
539 a47eb4c Dawsh 2y ago return config
540 a47eb4c Dawsh 2y ago
541 a47eb4c Dawsh 2y ago def add(self, remark: str, address: str, inbound: dict, settings: dict):
542 a47eb4c Dawsh 2y ago if inbound["protocol"] != "shadowsocks":
543 a47eb4c Dawsh 2y ago return
544 a47eb4c Dawsh 2y ago
545 a47eb4c Dawsh 2y ago outbound = self.make_outbound(
546 a47eb4c Dawsh 2y ago remark=remark,
547 a47eb4c Dawsh 2y ago address=address,
548 a47eb4c Dawsh 2y ago port=inbound["port"],
549 a47eb4c Dawsh 2y ago password=settings["password"],
550 a47eb4c Dawsh 2y ago method=settings["method"],
551 a47eb4c Dawsh 2y ago )
552 a47eb4c Dawsh 2y ago self.add_directly(outbound)
553 a47eb4c Dawsh 2y ago
554 a47eb4c Dawsh 2y ago
555 a47eb4c Dawsh 2y ago class SingBoxConfiguration(str):
556 a47eb4c Dawsh 2y ago def __init__(self):
557 a47eb4c Dawsh 2y ago template = render_template(SINGBOX_SUBSCRIPTION_TEMPLATE)
558 a47eb4c Dawsh 2y ago self.config = json.loads(template)
559 a47eb4c Dawsh 2y ago
560 a47eb4c Dawsh 2y ago def add_outbound(self, outbound_data):
561 a47eb4c Dawsh 2y ago self.config["outbounds"].append(outbound_data)
562 a47eb4c Dawsh 2y ago
563 a47eb4c Dawsh 2y ago def render(self):
564 a47eb4c Dawsh 2y ago urltest_types = ["vmess", "vless", "trojan", "shadowsocks"]
565 a47eb4c Dawsh 2y ago urltest_tags = [
566 a47eb4c Dawsh 2y ago outbound["tag"]
567 a47eb4c Dawsh 2y ago for outbound in self.config["outbounds"]
568 a47eb4c Dawsh 2y ago if outbound["type"] in urltest_types
569 a47eb4c Dawsh 2y ago ]
570 a47eb4c Dawsh 2y ago selector_types = ["vmess", "vless", "trojan", "shadowsocks", "urltest"]
571 a47eb4c Dawsh 2y ago selector_tags = [
572 a47eb4c Dawsh 2y ago outbound["tag"]
573 a47eb4c Dawsh 2y ago for outbound in self.config["outbounds"]
574 a47eb4c Dawsh 2y ago if outbound["type"] in selector_types
575 a47eb4c Dawsh 2y ago ]
576 a47eb4c Dawsh 2y ago
577 a47eb4c Dawsh 2y ago for outbound in self.config["outbounds"]:
578 a47eb4c Dawsh 2y ago if outbound.get("type") == "urltest":
579 a47eb4c Dawsh 2y ago outbound["outbounds"] = urltest_tags
580 a47eb4c Dawsh 2y ago
581 a47eb4c Dawsh 2y ago for outbound in self.config["outbounds"]:
582 a47eb4c Dawsh 2y ago if outbound.get("type") == "selector":
583 a47eb4c Dawsh 2y ago outbound["outbounds"] = selector_tags
584 a47eb4c Dawsh 2y ago
585 a47eb4c Dawsh 2y ago return json.dumps(self.config, indent=4)
586 a47eb4c Dawsh 2y ago
587 a47eb4c Dawsh 2y ago @staticmethod
588 a47eb4c Dawsh 2y ago def tls_config(
589 a47eb4c Dawsh 2y ago sni=None, fp=None, tls=None, pbk=None, sid=None, alpn=None, ais=None
590 a47eb4c Dawsh 2y ago ):
591 a47eb4c Dawsh 2y ago config = {}
592 a47eb4c Dawsh 2y ago if tls in ["tls", "reality"]:
593 a47eb4c Dawsh 2y ago config["enabled"] = True
594 a47eb4c Dawsh 2y ago
595 a47eb4c Dawsh 2y ago if sni is not None:
596 a47eb4c Dawsh 2y ago config["server_name"] = sni
597 a47eb4c Dawsh 2y ago
598 a47eb4c Dawsh 2y ago if tls == "tls" and ais:
599 a47eb4c Dawsh 2y ago config["insecure"] = ais
600 a47eb4c Dawsh 2y ago
601 a47eb4c Dawsh 2y ago if tls == "reality":
602 a47eb4c Dawsh 2y ago config["reality"] = {"enabled": True}
603 a47eb4c Dawsh 2y ago if pbk:
604 a47eb4c Dawsh 2y ago config["reality"]["public_key"] = pbk
605 a47eb4c Dawsh 2y ago if sid:
606 a47eb4c Dawsh 2y ago config["reality"]["short_id"] = sid
607 a47eb4c Dawsh 2y ago
608 a47eb4c Dawsh 2y ago if fp:
609 a47eb4c Dawsh 2y ago config["utls"] = {"enabled": bool(fp), "fingerprint": fp}
610 a47eb4c Dawsh 2y ago
611 a47eb4c Dawsh 2y ago if alpn:
612 a47eb4c Dawsh 2y ago config["alpn"] = [alpn] if not isinstance(alpn, list) else alpn
613 a47eb4c Dawsh 2y ago
614 a47eb4c Dawsh 2y ago return config
615 a47eb4c Dawsh 2y ago
616 a47eb4c Dawsh 2y ago @staticmethod
617 a47eb4c Dawsh 2y ago def transport_config(
618 a47eb4c Dawsh 2y ago transport_type="",
619 a47eb4c Dawsh 2y ago host="",
620 a47eb4c Dawsh 2y ago path="",
621 a47eb4c Dawsh 2y ago method="",
622 a47eb4c Dawsh 2y ago headers="",
623 a47eb4c Dawsh 2y ago idle_timeout="15s",
624 a47eb4c Dawsh 2y ago ping_timeout="15s",
625 a47eb4c Dawsh 2y ago max_early_data=None,
626 a47eb4c Dawsh 2y ago early_data_header_name=None,
627 a47eb4c Dawsh 2y ago permit_without_stream=False,
628 a47eb4c Dawsh 2y ago ):
629 a47eb4c Dawsh 2y ago transport_config = {}
630 a47eb4c Dawsh 2y ago
631 a47eb4c Dawsh 2y ago if transport_type:
632 a47eb4c Dawsh 2y ago transport_config["type"] = transport_type
633 a47eb4c Dawsh 2y ago
634 a47eb4c Dawsh 2y ago if transport_type == "http":
635 a47eb4c Dawsh 2y ago if host:
636 a47eb4c Dawsh 2y ago transport_config["host"] = host
637 a47eb4c Dawsh 2y ago if path:
638 a47eb4c Dawsh 2y ago transport_config["path"] = path
639 a47eb4c Dawsh 2y ago if method:
640 a47eb4c Dawsh 2y ago transport_config["method"] = method
641 a47eb4c Dawsh 2y ago if headers:
642 a47eb4c Dawsh 2y ago transport_config["headers"] = headers
643 a47eb4c Dawsh 2y ago if idle_timeout:
644 a47eb4c Dawsh 2y ago transport_config["idle_timeout"] = idle_timeout
645 a47eb4c Dawsh 2y ago if ping_timeout:
646 a47eb4c Dawsh 2y ago transport_config["ping_timeout"] = ping_timeout
647 a47eb4c Dawsh 2y ago
648 a47eb4c Dawsh 2y ago elif transport_type == "ws":
649 a47eb4c Dawsh 2y ago if path:
650 a47eb4c Dawsh 2y ago transport_config["path"] = path
651 a47eb4c Dawsh 2y ago if headers:
652 a47eb4c Dawsh 2y ago transport_config["headers"] = headers
653 a47eb4c Dawsh 2y ago if max_early_data is not None:
654 a47eb4c Dawsh 2y ago transport_config["max_early_data"] = max_early_data
655 a47eb4c Dawsh 2y ago if early_data_header_name:
656 a47eb4c Dawsh 2y ago transport_config["early_data_header_name"] = early_data_header_name
657 a47eb4c Dawsh 2y ago
658 a47eb4c Dawsh 2y ago elif transport_type == "ws":
659 a47eb4c Dawsh 2y ago if path:
660 a47eb4c Dawsh 2y ago transport_config["service_name"] = path
661 a47eb4c Dawsh 2y ago if idle_timeout:
662 a47eb4c Dawsh 2y ago transport_config["idle_timeout"] = idle_timeout
663 a47eb4c Dawsh 2y ago if ping_timeout:
664 a47eb4c Dawsh 2y ago transport_config["ping_timeout"] = ping_timeout
665 a47eb4c Dawsh 2y ago if permit_without_stream:
666 a47eb4c Dawsh 2y ago transport_config["permit_without_stream"] = permit_without_stream
667 a47eb4c Dawsh 2y ago
668 a47eb4c Dawsh 2y ago return transport_config
669 a47eb4c Dawsh 2y ago
670 a47eb4c Dawsh 2y ago def make_outbound(
671 a47eb4c Dawsh 2y ago self,
672 a47eb4c Dawsh 2y ago type: str,
673 a47eb4c Dawsh 2y ago remark: str,
674 a47eb4c Dawsh 2y ago address: str,
675 a47eb4c Dawsh 2y ago port: int,
676 a47eb4c Dawsh 2y ago net="ws",
677 a47eb4c Dawsh 2y ago path="",
678 a47eb4c Dawsh 2y ago host="",
679 a47eb4c Dawsh 2y ago flow="",
680 a47eb4c Dawsh 2y ago tls="",
681 a47eb4c Dawsh 2y ago sni="",
682 a47eb4c Dawsh 2y ago fp="",
683 a47eb4c Dawsh 2y ago alpn="",
684 a47eb4c Dawsh 2y ago pbk="",
685 a47eb4c Dawsh 2y ago sid="",
686 a47eb4c Dawsh 2y ago headers="",
687 a47eb4c Dawsh 2y ago ais="",
688 a47eb4c Dawsh 2y ago ):
689 a47eb4c Dawsh 2y ago config = {
690 a47eb4c Dawsh 2y ago "type": type,
691 a47eb4c Dawsh 2y ago "tag": remark,
692 a47eb4c Dawsh 2y ago "server": address,
693 a47eb4c Dawsh 2y ago "server_port": port,
694 a47eb4c Dawsh 2y ago }
695 a47eb4c Dawsh 2y ago if net in ("tcp", "kcp") or headers != "http":
696 a47eb4c Dawsh 2y ago if flow:
697 a47eb4c Dawsh 2y ago config["flow"] = flow
698 a47eb4c Dawsh 2y ago
699 a47eb4c Dawsh 2y ago if net in ["http", "ws", "quic", "grpc", "h2"]:
700 a47eb4c Dawsh 2y ago if net == "h2":
701 a47eb4c Dawsh 2y ago net = "http"
702 a47eb4c Dawsh 2y ago alpn = "h2"
703 a47eb4c Dawsh 2y ago config["transport"] = self.transport_config(
704 a47eb4c Dawsh 2y ago transport_type=net, host=host, path=path, headers=headers
705 a47eb4c Dawsh 2y ago )
706 a47eb4c Dawsh 2y ago else:
707 a47eb4c Dawsh 2y ago config["network"]: net
708 a47eb4c Dawsh 2y ago
709 a47eb4c Dawsh 2y ago if tls in ("tls", "reality"):
710 a47eb4c Dawsh 2y ago config["tls"] = self.tls_config(
711 a47eb4c Dawsh 2y ago sni=sni, fp=fp, tls=tls, pbk=pbk, sid=sid, alpn=alpn, ais=ais
712 a47eb4c Dawsh 2y ago )
713 a47eb4c Dawsh 2y ago
714 a47eb4c Dawsh 2y ago return config
715 a47eb4c Dawsh 2y ago
716 a47eb4c Dawsh 2y ago def add(self, remark: str, address: str, inbound: dict, settings: dict):
717 a47eb4c Dawsh 2y ago outbound = self.make_outbound(
718 a47eb4c Dawsh 2y ago remark=remark,
719 a47eb4c Dawsh 2y ago type=inbound["protocol"],
720 a47eb4c Dawsh 2y ago address=address,
721 a47eb4c Dawsh 2y ago port=inbound["port"],
722 a47eb4c Dawsh 2y ago net=inbound["network"],
723 a47eb4c Dawsh 2y ago tls=(inbound["tls"]),
724 a47eb4c Dawsh 2y ago flow=settings.get("flow", ""),
725 a47eb4c Dawsh 2y ago sni=inbound["sni"],
726 a47eb4c Dawsh 2y ago host=inbound["host"],
727 a47eb4c Dawsh 2y ago path=inbound["path"],
728 a47eb4c Dawsh 2y ago alpn=inbound.get("alpn", ""),
729 a47eb4c Dawsh 2y ago fp=inbound.get("fp", ""),
730 a47eb4c Dawsh 2y ago pbk=inbound.get("pbk", ""),
731 a47eb4c Dawsh 2y ago sid=inbound.get("sid", ""),
732 a47eb4c Dawsh 2y ago headers=inbound["header_type"],
733 a47eb4c Dawsh 2y ago ais=inbound.get("ais", ""),
734 a47eb4c Dawsh 2y ago )
735 a47eb4c Dawsh 2y ago
736 a47eb4c Dawsh 2y ago if inbound["protocol"] == "vmess":
737 a47eb4c Dawsh 2y ago outbound["uuid"] = settings["id"]
738 a47eb4c Dawsh 2y ago
739 a47eb4c Dawsh 2y ago elif inbound["protocol"] == "vless":
740 a47eb4c Dawsh 2y ago outbound["uuid"] = settings["id"]
741 a47eb4c Dawsh 2y ago
742 a47eb4c Dawsh 2y ago elif inbound["protocol"] == "trojan":
743 a47eb4c Dawsh 2y ago outbound["password"] = settings["password"]
744 a47eb4c Dawsh 2y ago
745 a47eb4c Dawsh 2y ago elif inbound["protocol"] == "shadowsocks":
746 a47eb4c Dawsh 2y ago outbound["password"] = settings["password"]
747 a47eb4c Dawsh 2y ago outbound["method"] = settings["method"]
748 a47eb4c Dawsh 2y ago
749 a47eb4c Dawsh 2y ago self.add_outbound(outbound)
750 a47eb4c Dawsh 2y ago
751 a47eb4c Dawsh 2y ago
752 a47eb4c Dawsh 2y ago def generate_v2ray_links(inbounds: list, key: str, extra_data: dict) -> list:
753 a47eb4c Dawsh 2y ago format_variables = setup_format_variables(extra_data)
754 a47eb4c Dawsh 2y ago return process_inbounds_and_tags(inbounds, key, format_variables, mode="v2ray")
755 a47eb4c Dawsh 2y ago
756 a47eb4c Dawsh 2y ago
757 a47eb4c Dawsh 2y ago def generate_clash_subscription(
758 a47eb4c Dawsh 2y ago inbounds: dict, key: str, extra_data: dict, is_meta: bool = False
759 a47eb4c Dawsh 2y ago ) -> str:
760 a47eb4c Dawsh 2y ago if is_meta is True:
761 a47eb4c Dawsh 2y ago conf = ClashMetaConfiguration()
762 a47eb4c Dawsh 2y ago else:
763 a47eb4c Dawsh 2y ago conf = ClashConfiguration()
764 a47eb4c Dawsh 2y ago
765 a47eb4c Dawsh 2y ago format_variables = setup_format_variables(extra_data)
766 a47eb4c Dawsh 2y ago return process_inbounds_and_tags(
767 a47eb4c Dawsh 2y ago inbounds, key, format_variables, mode="clash", conf=conf
768 a47eb4c Dawsh 2y ago )
769 a47eb4c Dawsh 2y ago
770 a47eb4c Dawsh 2y ago
771 a47eb4c Dawsh 2y ago def generate_singbox_subscription(
772 a47eb4c Dawsh 2y ago inbounds: dict, key: str, extra_data: dict
773 a47eb4c Dawsh 2y ago ) -> str:
774 a47eb4c Dawsh 2y ago conf = SingBoxConfiguration()
775 a47eb4c Dawsh 2y ago
776 a47eb4c Dawsh 2y ago format_variables = setup_format_variables(extra_data)
777 a47eb4c Dawsh 2y ago return process_inbounds_and_tags(
778 a47eb4c Dawsh 2y ago inbounds, key, format_variables, mode="sing-box", conf=conf
779 a47eb4c Dawsh 2y ago )
780 a47eb4c Dawsh 2y ago
781 a47eb4c Dawsh 2y ago
782 a47eb4c Dawsh 2y ago def generate_v2ray_subscription(links: list) -> str:
783 a47eb4c Dawsh 2y ago return base64.b64encode("\n".join(links).encode()).decode()
784 a47eb4c Dawsh 2y ago
785 a47eb4c Dawsh 2y ago
786 a47eb4c Dawsh 2y ago def generate_outline_subscription(
787 a47eb4c Dawsh 2y ago inbounds: dict, key: str, extra_data: dict
788 a47eb4c Dawsh 2y ago ) -> str:
789 a47eb4c Dawsh 2y ago conf = OutlineConfiguration()
790 a47eb4c Dawsh 2y ago
791 a47eb4c Dawsh 2y ago format_variables = setup_format_variables(extra_data)
792 a47eb4c Dawsh 2y ago return process_inbounds_and_tags(
793 a47eb4c Dawsh 2y ago inbounds, key, format_variables, mode="outline", conf=conf
794 a47eb4c Dawsh 2y ago )
795 a47eb4c Dawsh 2y ago
796 a47eb4c Dawsh 2y ago
797 a47eb4c Dawsh 2y ago def generate_subscription(
798 a47eb4c Dawsh 2y ago user: "UserResponse",
799 a47eb4c Dawsh 2y ago config_format: Literal["v2ray", "clash-meta", "clash", "sing-box", "outline"],
800 a47eb4c Dawsh 2y ago as_base64: bool,
801 a47eb4c Dawsh 2y ago ) -> str:
802 a47eb4c Dawsh 2y ago kwargs = {
803 a47eb4c Dawsh 2y ago # "proxies": user.proxies,
804 a47eb4c Dawsh 2y ago "inbounds": user.inbounds,
805 a47eb4c Dawsh 2y ago "key": user.key,
806 a47eb4c Dawsh 2y ago "extra_data": user.__dict__,
807 a47eb4c Dawsh 2y ago }
808 a47eb4c Dawsh 2y ago
809 a47eb4c Dawsh 2y ago if config_format == "v2ray":
810 a47eb4c Dawsh 2y ago config = "\n".join(generate_v2ray_links(**kwargs))
811 a47eb4c Dawsh 2y ago elif config_format == "clash-meta":
812 a47eb4c Dawsh 2y ago config = generate_clash_subscription(**kwargs, is_meta=True)
813 a47eb4c Dawsh 2y ago elif config_format == "clash":
814 a47eb4c Dawsh 2y ago config = generate_clash_subscription(**kwargs)
815 a47eb4c Dawsh 2y ago elif config_format == "sing-box":
816 a47eb4c Dawsh 2y ago config = generate_singbox_subscription(**kwargs)
817 a47eb4c Dawsh 2y ago elif config_format == "outline":
818 a47eb4c Dawsh 2y ago config = generate_outline_subscription(**kwargs)
819 a47eb4c Dawsh 2y ago else:
820 a47eb4c Dawsh 2y ago raise ValueError(f'Unsupported format "{config_format}"')
821 a47eb4c Dawsh 2y ago
822 a47eb4c Dawsh 2y ago if as_base64:
823 a47eb4c Dawsh 2y ago config = base64.b64encode(config.encode()).decode()
824 a47eb4c Dawsh 2y ago
825 a47eb4c Dawsh 2y ago return config
826 a47eb4c Dawsh 2y ago
827 a47eb4c Dawsh 2y ago
828 a47eb4c Dawsh 2y ago def format_time_left(seconds_left: int) -> str:
829 a47eb4c Dawsh 2y ago if not seconds_left or seconds_left <= 0:
830 a47eb4c Dawsh 2y ago return "∞"
831 a47eb4c Dawsh 2y ago
832 a47eb4c Dawsh 2y ago minutes, seconds = divmod(seconds_left, 60)
833 a47eb4c Dawsh 2y ago hours, minutes = divmod(minutes, 60)
834 a47eb4c Dawsh 2y ago days, hours = divmod(hours, 24)
835 a47eb4c Dawsh 2y ago months, days = divmod(days, 30)
836 a47eb4c Dawsh 2y ago
837 a47eb4c Dawsh 2y ago result = []
838 a47eb4c Dawsh 2y ago if months:
839 a47eb4c Dawsh 2y ago result.append(f"{months}m")
840 a47eb4c Dawsh 2y ago if days:
841 a47eb4c Dawsh 2y ago result.append(f"{days}d")
842 a47eb4c Dawsh 2y ago if hours and (days < 7):
843 a47eb4c Dawsh 2y ago result.append(f"{hours}h")
844 a47eb4c Dawsh 2y ago if minutes and not (months or days):
845 a47eb4c Dawsh 2y ago result.append(f"{minutes}m")
846 a47eb4c Dawsh 2y ago if seconds and not (months or days):
847 a47eb4c Dawsh 2y ago result.append(f"{seconds}s")
848 a47eb4c Dawsh 2y ago return " ".join(result)
849 a47eb4c Dawsh 2y ago
850 a47eb4c Dawsh 2y ago from app.models.user import UserStatus
851 a47eb4c Dawsh 2y ago
852 a47eb4c Dawsh 2y ago def setup_format_variables(extra_data: dict) -> dict:
853 a47eb4c Dawsh 2y ago
854 a47eb4c Dawsh 2y ago
855 a47eb4c Dawsh 2y ago user_status = extra_data.get("status")
856 a47eb4c Dawsh 2y ago expire_timestamp = extra_data.get("expire")
857 a47eb4c Dawsh 2y ago on_hold_expire_duration = extra_data.get("on_hold_expire_duration")
858 a47eb4c Dawsh 2y ago
859 a47eb4c Dawsh 2y ago if user_status != UserStatus.on_hold:
860 a47eb4c Dawsh 2y ago if expire_timestamp is not None and expire_timestamp >= 0:
861 a47eb4c Dawsh 2y ago seconds_left = expire_timestamp - int(dt.utcnow().timestamp())
862 a47eb4c Dawsh 2y ago expire_datetime = dt.fromtimestamp(expire_timestamp)
863 a47eb4c Dawsh 2y ago expire_date = expire_datetime.date()
864 a47eb4c Dawsh 2y ago jalali_expire_date = jd.fromgregorian(
865 a47eb4c Dawsh 2y ago year=expire_date.year, month=expire_date.month, day=expire_date.day
866 a47eb4c Dawsh 2y ago ).strftime("%Y-%m-%d")
867 a47eb4c Dawsh 2y ago days_left = (expire_datetime - dt.utcnow()).days + 1
868 a47eb4c Dawsh 2y ago time_left = format_time_left(seconds_left)
869 a47eb4c Dawsh 2y ago else:
870 a47eb4c Dawsh 2y ago days_left = "∞"
871 a47eb4c Dawsh 2y ago time_left = "∞"
872 a47eb4c Dawsh 2y ago expire_date = "∞"
873 a47eb4c Dawsh 2y ago jalali_expire_date = "∞"
874 a47eb4c Dawsh 2y ago else:
875 a47eb4c Dawsh 2y ago if on_hold_expire_duration is not None and on_hold_expire_duration >= 0:
876 a47eb4c Dawsh 2y ago days_left = timedelta(seconds=on_hold_expire_duration).days
877 a47eb4c Dawsh 2y ago time_left = format_time_left(on_hold_expire_duration)
878 a47eb4c Dawsh 2y ago expire_date = "-"
879 a47eb4c Dawsh 2y ago jalali_expire_date = "-"
880 a47eb4c Dawsh 2y ago else:
881 a47eb4c Dawsh 2y ago days_left = "∞"
882 a47eb4c Dawsh 2y ago time_left = "∞"
883 a47eb4c Dawsh 2y ago expire_date = "∞"
884 a47eb4c Dawsh 2y ago jalali_expire_date = "∞"
885 a47eb4c Dawsh 2y ago
886 a47eb4c Dawsh 2y ago if extra_data.get("data_limit"):
887 a47eb4c Dawsh 2y ago data_limit = readable_size(extra_data["data_limit"])
888 a47eb4c Dawsh 2y ago data_left = extra_data["data_limit"] - extra_data["used_traffic"]
889 a47eb4c Dawsh 2y ago if data_left < 0:
890 a47eb4c Dawsh 2y ago data_left = 0
891 a47eb4c Dawsh 2y ago data_left = readable_size(data_left)
892 a47eb4c Dawsh 2y ago else:
893 a47eb4c Dawsh 2y ago data_limit = "∞"
894 a47eb4c Dawsh 2y ago data_left = "∞"
895 a47eb4c Dawsh 2y ago
896 a47eb4c Dawsh 2y ago status_emoji = STATUS_EMOJIS.get(extra_data.get("status")) or ""
897 a47eb4c Dawsh 2y ago
898 a47eb4c Dawsh 2y ago format_variables = {
899 a47eb4c Dawsh 2y ago "SERVER_IP": SERVER_IP,
900 a47eb4c Dawsh 2y ago "USERNAME": extra_data.get("username", "{USERNAME}"),
901 a47eb4c Dawsh 2y ago "DATA_USAGE": readable_size(extra_data.get("used_traffic")),
902 a47eb4c Dawsh 2y ago "DATA_LIMIT": data_limit,
903 a47eb4c Dawsh 2y ago "DATA_LEFT": data_left,
904 a47eb4c Dawsh 2y ago "DAYS_LEFT": days_left,
905 a47eb4c Dawsh 2y ago "EXPIRE_DATE": expire_date,
906 a47eb4c Dawsh 2y ago "JALALI_EXPIRE_DATE": jalali_expire_date,
907 a47eb4c Dawsh 2y ago "TIME_LEFT": time_left,
908 a47eb4c Dawsh 2y ago "STATUS_EMOJI": status_emoji,
909 a47eb4c Dawsh 2y ago }
910 a47eb4c Dawsh 2y ago
911 a47eb4c Dawsh 2y ago return format_variables
912 a47eb4c Dawsh 2y ago
913 a47eb4c Dawsh 2y ago
914 a47eb4c Dawsh 2y ago def process_inbounds_and_tags(
915 a47eb4c Dawsh 2y ago inbounds: list,
916 a47eb4c Dawsh 2y ago ukey: str,
917 a47eb4c Dawsh 2y ago format_variables: dict,
918 a47eb4c Dawsh 2y ago mode: str = "v2ray",
919 a47eb4c Dawsh 2y ago conf=None,
920 a47eb4c Dawsh 2y ago ) -> Union[List, str]:
921 a47eb4c Dawsh 2y ago salt = secrets.token_hex(8)
922 a47eb4c Dawsh 2y ago results = []
923 a47eb4c Dawsh 2y ago
924 a47eb4c Dawsh 2y ago for inb in inbounds:
925 4d647dd Dawsh 2y ago inb_id, inbound, protocol, tag = inb.id, json.loads(inb.config), inb.protocol, inb.tag
926 a47eb4c Dawsh 2y ago settings = generate_settings(ukey, protocol)
927 a47eb4c Dawsh 2y ago
928 a47eb4c Dawsh 2y ago format_variables.update({"PROTOCOL": protocol.name})
929 a47eb4c Dawsh 2y ago if not inbound:
930 a47eb4c Dawsh 2y ago continue
931 a47eb4c Dawsh 2y ago
932 a47eb4c Dawsh 2y ago format_variables.update({"TRANSPORT": inbound["network"]})
933 a47eb4c Dawsh 2y ago host_inbound = inbound.copy()
934 a47eb4c Dawsh 2y ago with GetDB() as db:
935 a47eb4c Dawsh 2y ago hosts = crud.get_inbound_hosts(db, inb_id)
936 a47eb4c Dawsh 2y ago # for host in xray.hosts.get(tag, []):
937 a47eb4c Dawsh 2y ago for host in hosts:
938 fe923dc Dawsh 2y ago host_snis = host.sni.split(",") if host.sni else []
939 fe923dc Dawsh 2y ago sni_list = host_snis or inbound["sni"]
940 a47eb4c Dawsh 2y ago if sni_list:
941 a47eb4c Dawsh 2y ago sni = random.choice(sni_list).replace("*", salt)
942 fe923dc Dawsh 2y ago else:
943 fe923dc Dawsh 2y ago sni = ""
944 a47eb4c Dawsh 2y ago
945 fe923dc Dawsh 2y ago host_hosts = host.host.split(",") if host.host else []
946 fe923dc Dawsh 2y ago req_host_list = host_hosts or inbound["host"]
947 a47eb4c Dawsh 2y ago if req_host_list:
948 a47eb4c Dawsh 2y ago req_host = random.choice(req_host_list).replace("*", salt)
949 fe923dc Dawsh 2y ago else:
950 fe923dc Dawsh 2y ago req_host = ""
951 fe923dc Dawsh 2y ago
952 a47eb4c Dawsh 2y ago host_tls = None if host.security == InboundHostSecurity.inbound_default else host.security.value
953 a47eb4c Dawsh 2y ago host_inbound.update(
954 a47eb4c Dawsh 2y ago {
955 a47eb4c Dawsh 2y ago "port": host.port or inbound["port"],
956 a47eb4c Dawsh 2y ago "sni": sni,
957 a47eb4c Dawsh 2y ago "host": req_host,
958 a47eb4c Dawsh 2y ago "tls": inbound["tls"] if host_tls is None else host_tls,
959 a47eb4c Dawsh 2y ago "alpn": host.alpn.value or inbound.get("alpn", ""),
960 a47eb4c Dawsh 2y ago "path": inbound.get("path", "")
961 a47eb4c Dawsh 2y ago if host.path is None
962 a47eb4c Dawsh 2y ago else host.path.format_map(format_variables),
963 a47eb4c Dawsh 2y ago "fp": host.fingerprint.value or inbound.get("fp", ""),
964 a47eb4c Dawsh 2y ago "ais": host.allowinsecure or ""
965 a47eb4c Dawsh 2y ago }
966 a47eb4c Dawsh 2y ago )
967 a47eb4c Dawsh 2y ago
968 a47eb4c Dawsh 2y ago if mode == "v2ray":
969 a47eb4c Dawsh 2y ago results.append(
970 a47eb4c Dawsh 2y ago get_v2ray_link(
971 a47eb4c Dawsh 2y ago remark=host.remark.format_map(format_variables),
972 a47eb4c Dawsh 2y ago address=host.address.format_map(
973 a47eb4c Dawsh 2y ago format_variables),
974 a47eb4c Dawsh 2y ago inbound=host_inbound,
975 a47eb4c Dawsh 2y ago settings=settings,
976 a47eb4c Dawsh 2y ago )
977 a47eb4c Dawsh 2y ago )
978 a47eb4c Dawsh 2y ago elif mode in ["clash", "sing-box", "outline"]:
979 a47eb4c Dawsh 2y ago conf.add(
980 a47eb4c Dawsh 2y ago remark=host.remark.format_map(format_variables),
981 a47eb4c Dawsh 2y ago address=host.address.format_map(format_variables),
982 a47eb4c Dawsh 2y ago inbound=host_inbound,
983 a47eb4c Dawsh 2y ago settings=settings,
984 a47eb4c Dawsh 2y ago )
985 a47eb4c Dawsh 2y ago
986 a47eb4c Dawsh 2y ago if mode in ["clash", "sing-box", "outline"]:
987 a47eb4c Dawsh 2y ago return conf.render()
988 a47eb4c Dawsh 2y ago return results
989 a47eb4c Dawsh 2y ago
990 a47eb4c Dawsh 2y ago
991 a47eb4c Dawsh 2y ago def encode_title(text: str) -> str:
992 a47eb4c Dawsh 2y ago return f"base64:{base64.b64encode(text.encode()).decode()}"