From 010bc66ccf9296682ed95f08b2b0049ab18b4d37 Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Tue, 18 Jul 2023 17:17:46 +0800 Subject: [PATCH 1/4] create --- clashbyte/__init__.py | 4 +- clashbyte/apis.py | 7 +- clashbyte/component/__init__.py | 9 +++ clashbyte/component/convertor.py | 29 ++++++++ clashbyte/panel.py | 60 ++++++++++++++++ clashbyte/scheme/__init__.py | 5 ++ clashbyte/scheme/_default.yaml | 16 +++++ clashbyte/scheme/hysteria.py | 107 +++++++++++++++++++++++++++++ clashbyte/scheme/tuic.py | 5 ++ clashbyte/{toolbox.py => utils.py} | 20 +++--- 10 files changed, 243 insertions(+), 19 deletions(-) create mode 100644 clashbyte/component/__init__.py create mode 100644 clashbyte/component/convertor.py create mode 100644 clashbyte/panel.py create mode 100644 clashbyte/scheme/__init__.py create mode 100644 clashbyte/scheme/_default.yaml create mode 100644 clashbyte/scheme/hysteria.py create mode 100644 clashbyte/scheme/tuic.py rename clashbyte/{toolbox.py => utils.py} (84%) diff --git a/clashbyte/__init__.py b/clashbyte/__init__.py index b38210c..1a1f82e 100644 --- a/clashbyte/__init__.py +++ b/clashbyte/__init__.py @@ -4,7 +4,7 @@ # Github : https://github.com/QIN2DIM # Description: from .apis import ClashMetaAPI -from .toolbox import init_log, project +from .utils import init_log, project __all__ = ["ClashMetaAPI", "init_log", "project"] -__version__ = "0.0.5" +__version__ = "0.1.0" diff --git a/clashbyte/apis.py b/clashbyte/apis.py index 9f9029b..ef95caa 100644 --- a/clashbyte/apis.py +++ b/clashbyte/apis.py @@ -21,17 +21,13 @@ class Clash: @dataclass -class ClashMeta: +class ClashMetaAPI: """https://wiki.metacubex.one/api/""" - -@dataclass -class ClashMetaAPI: secret: str = "" controller_url: str = "" _client = None - _core = None def __post_init__(self): if not self.controller_url: @@ -48,7 +44,6 @@ def __post_init__(self): ) del headers["Authorization"] self._client: httpx.Client = httpx.Client(base_url=self.controller_url, headers=headers) - self._core: ClashMeta = ClashMeta() @property def is_alive(self): diff --git a/clashbyte/component/__init__.py b/clashbyte/component/__init__.py new file mode 100644 index 0000000..4ebf0fc --- /dev/null +++ b/clashbyte/component/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 16:48 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: + + +if __name__ == "__main__": + pass diff --git a/clashbyte/component/convertor.py b/clashbyte/component/convertor.py new file mode 100644 index 0000000..1222594 --- /dev/null +++ b/clashbyte/component/convertor.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Time : 2022/5/22 10:43 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +from __future__ import annotations + +from dataclasses import dataclass +from typing import List, Dict +from urllib.parse import urlparse + +from clashbyte.scheme.hysteria import Hysteria + + +@dataclass +class Toolkit: + @staticmethod + def parse_hysteria_sharelink(links: List[str] | str) -> Dict[str, Hysteria | None]: + if isinstance(links, str): + links = [links] + response = {} + for link in links: + u = urlparse(link) + if u and u.scheme == "hysteria": + hysteria = Hysteria.from_sharelink(u) + response[link] = hysteria + else: + response[link] = None + return response diff --git a/clashbyte/panel.py b/clashbyte/panel.py new file mode 100644 index 0000000..2ca8e8f --- /dev/null +++ b/clashbyte/panel.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 16:43 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +from __future__ import annotations + +from dataclasses import dataclass + +import pywebio +from pywebio.input import * +from pywebio.output import * + +pywebio.config(theme="minty") + + +@dataclass +class Panel: + TITLE = "云彩姬@订阅转换" + + def startup(self): + """ + actions("\n", buttons=[ + {"label": "生成订阅链接", "value": "生成订阅链接", "color": "secondary"}, + {"label": "生成短链接", "value": "生成短链接", "color": "secondary"}, + ], name="action_link"), + actions("\n", buttons=[ + {"label": "上传配置", "value": "上传配置", "color": "info"}, + {"label": "一键导入Clash", "value": "一键导入Clash", "color": "info"}, + ], name="action_inner") + """ + toast("Just a demo", position="right", duration=3, color="##78c2ad") + + data = input_group( + label=self.TITLE, + inputs=[ + textarea( + label="订阅链接", + placeholder="支持 hysteria 链接,多个链接每行一个或用 | 分隔", + rows=4, + name="sharelink", + ), + select(label="客户端", options=["NekoRay", "Clash Verge"], name="client"), + actions( + label="label", + buttons=[ + {"label": "上传配置", "value": "上传配置", "color": "info"}, + {"label": "一键导入 Clash", "value": "一键导入 Clash", "color": "info"}, + ], + name="clash_config", + ), + ], + ) + + put_text(data) + + +if __name__ == "__main__": + panel = Panel() + panel.startup() diff --git a/clashbyte/scheme/__init__.py b/clashbyte/scheme/__init__.py new file mode 100644 index 0000000..117f90f --- /dev/null +++ b/clashbyte/scheme/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 16:45 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: diff --git a/clashbyte/scheme/_default.yaml b/clashbyte/scheme/_default.yaml new file mode 100644 index 0000000..e9f654b --- /dev/null +++ b/clashbyte/scheme/_default.yaml @@ -0,0 +1,16 @@ +dns: + enable: true + listen: 0.0.0.0:53 + enhanced-mode: fake-ip + nameserver: + - "https://dns.google/dns-query#PROXY" + - "https://security.cloudflare-dns.com/dns-query#PROXY" + proxy-server-nameserver: + - system + nameserver-policy: + "geosite:cn": + - system +rules: + - GEOSITE,CN,DIRECT + - GEOIP,CN,DIRECT + - MATCH,PROXY \ No newline at end of file diff --git a/clashbyte/scheme/hysteria.py b/clashbyte/scheme/hysteria.py new file mode 100644 index 0000000..e63756b --- /dev/null +++ b/clashbyte/scheme/hysteria.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 16:45 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal +from urllib.parse import urlparse + +from clashbyte.utils import from_dict_to_cls + + +@dataclass +class Hysteria: + # hostname or IP address of the server to connect to (required) + host: str + + # port of the server to connect to (required) + port: int + + # upstream bandwidth in Mbps (required) + upmbps: int + + # downstream bandwidth in Mbps (required) + downmbps: int + + # multiport skip (optional) + mport: str = "" + + # protocol to use ("udp", "wechat-video", "faketcp") (optional, default: "udp") + protocol: Literal["udp", "wechat-video", "faketcp"] = "udp" + + # authentication payload (string) (optional) + auth: str = "" + + # SNI for TLS (optional) + peer: str = "" + + # insecure: ignore certificate errors (optional) + insecure: str = "" + + # QUIC ALPN (optional) + alpn: str = "h3" + + # Obfuscation mode (optional, empty or "xplus") + obfs: Literal["", "xplus"] = "" + + # Obfuscation password (optional) + obfsParam: str = "" + + # remarks alias (optional) + remarks: str = "" + + @classmethod + def from_sharelink(cls, link: str) -> Hysteria | None: + """ + 从节点分享链接反序列化节点对象 + + https://hysteria.network/zh/docs/uri-scheme/ + + :param link: Hysteria URL Scheme + :return: + """ + u = urlparse(link) + host, port = u.netloc.split(":") + data = {"host": host, "port": port, "remarks": u.fragment} + for e in u.query.split("&"): + k, v = e.split("=") + data[k] = v + return from_dict_to_cls(cls, data) + + def to_sharelink(self) -> str: + t = "hysteria://{netloc}?{query}#{fragment}" + netloc = f"{self.host}:{self.port}" + queries = [] + for k in self.__dict__: + if not self.__dict__[k]: + continue + v = self.__dict__[k] + queries.append(f"{k}={v}") + query = "&".join(queries) + fragment = self.remarks + sharelink = t.format(netloc=netloc, query=query, fragment=fragment) + return sharelink + + def to_clash_node(self, **kwargs): + self.remarks = self.remarks or self.host + node = { + "name": self.remarks, + "type": "hysteria", + "server": self.host, + "port": self.port, + "ports": self.mport, + "alpn": [self.alpn], + "protocol": self.protocol, + "up": self.upmbps, + "down": self.downmbps, + "sni": self.peer, + "skip-cert-verify": self.insecure, + "auth_str": self.auth, + "obfs": self.obfsParam, # auto fill + } + if kwargs: + node.update(**kwargs) + return node diff --git a/clashbyte/scheme/tuic.py b/clashbyte/scheme/tuic.py new file mode 100644 index 0000000..64a2cca --- /dev/null +++ b/clashbyte/scheme/tuic.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 16:47 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: diff --git a/clashbyte/toolbox.py b/clashbyte/utils.py similarity index 84% rename from clashbyte/toolbox.py rename to clashbyte/utils.py index 6b150b5..90727f9 100644 --- a/clashbyte/toolbox.py +++ b/clashbyte/utils.py @@ -5,7 +5,7 @@ # Description: from __future__ import annotations -import os +import inspect import sys from dataclasses import dataclass from os.path import dirname @@ -14,8 +14,6 @@ from loguru import logger -__all__ = ["project", "init_log"] - @dataclass class Project: @@ -25,8 +23,14 @@ class Project: logs = root_point.joinpath("logs") - def __post_init__(self): - os.makedirs(self.database, exist_ok=True) + +def from_dict_to_cls(cls, data): + return cls( + **{ + key: (data[key] if val.default == val.empty else data.get(key, val.default)) + for key, val in inspect.signature(cls).parameters.items() + } + ) def init_log(*, stdout_level: Literal["INFO", "DEBUG"] = "DEBUG", **sink_channel): @@ -68,9 +72,3 @@ def init_log(*, stdout_level: Literal["INFO", "DEBUG"] = "DEBUG", **sink_channel project = Project() -init_log( - stdout_level="INFO", - # error=project.logs.joinpath("error.log"), - # runtime=project.logs.joinpath("runtime.log"), - # serialize=project.logs.joinpath("serialize.log"), -) From d493519b4ea4fa4e0e36b4541226fd7b9f1347a7 Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Tue, 18 Jul 2023 18:25:12 +0800 Subject: [PATCH 2/4] build scheme --- clashbyte/component/__init__.py | 6 +++--- clashbyte/component/convertor.py | 29 +++++++++++++++-------------- clashbyte/scheme/__init__.py | 5 +++++ clashbyte/scheme/_scheme.py | 28 ++++++++++++++++++++++++++++ clashbyte/scheme/hysteria.py | 16 ++++++++-------- clashbyte/scheme/tuic.py | 20 ++++++++++++++++++++ 6 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 clashbyte/scheme/_scheme.py diff --git a/clashbyte/component/__init__.py b/clashbyte/component/__init__.py index 4ebf0fc..f17f9ed 100644 --- a/clashbyte/component/__init__.py +++ b/clashbyte/component/__init__.py @@ -3,7 +3,7 @@ # Author : QIN2DIM # Github : https://github.com/QIN2DIM # Description: +from .convertor import Toolkit - -if __name__ == "__main__": - pass +kit = Toolkit +__all__ = ["kit"] diff --git a/clashbyte/component/convertor.py b/clashbyte/component/convertor.py index 1222594..47b7d6e 100644 --- a/clashbyte/component/convertor.py +++ b/clashbyte/component/convertor.py @@ -6,24 +6,25 @@ from __future__ import annotations from dataclasses import dataclass -from typing import List, Dict from urllib.parse import urlparse -from clashbyte.scheme.hysteria import Hysteria +from clashbyte.scheme import Hysteria +from clashbyte.scheme import Scheme +from clashbyte.scheme import Tuic @dataclass class Toolkit: @staticmethod - def parse_hysteria_sharelink(links: List[str] | str) -> Dict[str, Hysteria | None]: - if isinstance(links, str): - links = [links] - response = {} - for link in links: - u = urlparse(link) - if u and u.scheme == "hysteria": - hysteria = Hysteria.from_sharelink(u) - response[link] = hysteria - else: - response[link] = None - return response + def from_link_to_scheme(link: str) -> Scheme | None: + parser = urlparse(link) + if not parser: + return + if parser.scheme == "hysteria": + return Hysteria.from_urlparser(parser) + if parser.scheme == "tuic": + return Tuic.from_urlparser(parser) + + @staticmethod + def from_clash_to_links(): + pass diff --git a/clashbyte/scheme/__init__.py b/clashbyte/scheme/__init__.py index 117f90f..67d90e7 100644 --- a/clashbyte/scheme/__init__.py +++ b/clashbyte/scheme/__init__.py @@ -3,3 +3,8 @@ # Author : QIN2DIM # Github : https://github.com/QIN2DIM # Description: +from ._scheme import Scheme +from .hysteria import Hysteria +from .tuic import Tuic + +__all__ = ["Hysteria", "Tuic", "Scheme"] diff --git a/clashbyte/scheme/_scheme.py b/clashbyte/scheme/_scheme.py new file mode 100644 index 0000000..9f7d6b1 --- /dev/null +++ b/clashbyte/scheme/_scheme.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Time : 2023/7/18 17:26 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, Any +from urllib.parse import ParseResult + + +@dataclass +class Scheme(ABC): + path_clash_config = Path("_default.yaml") + + @classmethod + @abstractmethod + def from_urlparser(cls, parser: ParseResult): + ... + + @abstractmethod + def to_sharelink(self) -> str: + ... + + @abstractmethod + def to_clash_node(self, **kwargs) -> Dict[str, Any]: + ... diff --git a/clashbyte/scheme/hysteria.py b/clashbyte/scheme/hysteria.py index e63756b..d33dfaf 100644 --- a/clashbyte/scheme/hysteria.py +++ b/clashbyte/scheme/hysteria.py @@ -7,13 +7,14 @@ from dataclasses import dataclass from typing import Literal -from urllib.parse import urlparse +from urllib.parse import ParseResult +from clashbyte.scheme._scheme import Scheme from clashbyte.utils import from_dict_to_cls @dataclass -class Hysteria: +class Hysteria(Scheme): # hostname or IP address of the server to connect to (required) host: str @@ -54,19 +55,18 @@ class Hysteria: remarks: str = "" @classmethod - def from_sharelink(cls, link: str) -> Hysteria | None: + def from_urlparser(cls, parser: ParseResult): """ 从节点分享链接反序列化节点对象 https://hysteria.network/zh/docs/uri-scheme/ - :param link: Hysteria URL Scheme + :param parser: Hysteria URL Scheme Parser :return: """ - u = urlparse(link) - host, port = u.netloc.split(":") - data = {"host": host, "port": port, "remarks": u.fragment} - for e in u.query.split("&"): + host, port = parser.netloc.split(":") + data = {"host": host, "port": port, "remarks": parser.fragment} + for e in parser.query.split("&"): k, v = e.split("=") data[k] = v return from_dict_to_cls(cls, data) diff --git a/clashbyte/scheme/tuic.py b/clashbyte/scheme/tuic.py index 64a2cca..b5017b7 100644 --- a/clashbyte/scheme/tuic.py +++ b/clashbyte/scheme/tuic.py @@ -3,3 +3,23 @@ # Author : QIN2DIM # Github : https://github.com/QIN2DIM # Description: +from __future__ import annotations + +from dataclasses import dataclass +from typing import Dict, Any +from urllib.parse import ParseResult + +from clashbyte.scheme._scheme import Scheme + + +@dataclass +class Tuic(Scheme): + @classmethod + def from_urlparser(cls, parser: ParseResult): + pass + + def to_sharelink(self) -> str: + pass + + def to_clash_node(self, **kwargs) -> Dict[str, Any]: + pass From 5a713941ea5148b35d00416b958605ed8f6f08cf Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:27:01 +0800 Subject: [PATCH 3/4] feat(add): hysteria node parser from hysteria sharelink to clash configuration --- clashbyte/component/convertor.py | 34 ++++++++++++++++++++++++++++++++ clashbyte/scheme/__init__.py | 4 ++-- clashbyte/scheme/_default.yaml | 21 ++++++++++++++++---- clashbyte/scheme/_scheme.py | 4 ++-- clashbyte/scheme/hysteria.py | 17 ++++++++-------- requirements-dev.txt | 5 +++-- setup.py | 2 +- 7 files changed, 68 insertions(+), 19 deletions(-) diff --git a/clashbyte/component/convertor.py b/clashbyte/component/convertor.py index 47b7d6e..976b800 100644 --- a/clashbyte/component/convertor.py +++ b/clashbyte/component/convertor.py @@ -5,12 +5,19 @@ # Description: from __future__ import annotations +import os +import shutil from dataclasses import dataclass +from pathlib import Path +from typing import List from urllib.parse import urlparse +import yaml + from clashbyte.scheme import Hysteria from clashbyte.scheme import Scheme from clashbyte.scheme import Tuic +from scheme import path_clash_config @dataclass @@ -28,3 +35,30 @@ def from_link_to_scheme(link: str) -> Scheme | None: @staticmethod def from_clash_to_links(): pass + + @staticmethod + def gen_clash_config_from_links(links: str | List[str], sp: Path | str | None = None): + if isinstance(links, str): + links = links.split("\n") + proxies = [Toolkit.from_link_to_scheme(link) for link in links] + proxies = [p.to_clash_node() for p in proxies if p] + proxy_groups = [ + {"name": "PROXY", "type": "select", "proxies": [p["name"] for p in proxies]} + ] + + if sp is None: + sp = Path("clash_configs") + elif isinstance(sp, str): + sp = Path(sp) + os.makedirs(sp, exist_ok=True) + + t = yaml.safe_load(path_clash_config.read_text(encoding="utf8")) + t.update({"proxies": proxies, "proxy-groups": proxy_groups}) + fn = "runtime.yaml" + fp = sp.joinpath(fn) + if fp.exists(): + shutil.move(fp, sp.joinpath(f"{int(fp.stat().st_mtime)}.yaml")) + Path(fn).write_text(yaml.safe_dump(t, allow_unicode=True, sort_keys=False)) + shutil.copy(fn, fp) + + return t diff --git a/clashbyte/scheme/__init__.py b/clashbyte/scheme/__init__.py index 67d90e7..59ff0a0 100644 --- a/clashbyte/scheme/__init__.py +++ b/clashbyte/scheme/__init__.py @@ -3,8 +3,8 @@ # Author : QIN2DIM # Github : https://github.com/QIN2DIM # Description: -from ._scheme import Scheme +from ._scheme import Scheme, path_clash_config from .hysteria import Hysteria from .tuic import Tuic -__all__ = ["Hysteria", "Tuic", "Scheme"] +__all__ = ["Hysteria", "Tuic", "Scheme", "path_clash_config"] diff --git a/clashbyte/scheme/_default.yaml b/clashbyte/scheme/_default.yaml index e9f654b..22c1051 100644 --- a/clashbyte/scheme/_default.yaml +++ b/clashbyte/scheme/_default.yaml @@ -1,16 +1,29 @@ dns: enable: true - listen: 0.0.0.0:53 enhanced-mode: fake-ip nameserver: - "https://dns.google/dns-query#PROXY" - "https://security.cloudflare-dns.com/dns-query#PROXY" + - "quic://dns.adguard-dns.com" proxy-server-nameserver: - system + - https://223.5.5.5/dns-query nameserver-policy: - "geosite:cn": + geosite:cn: - system + - https://223.5.5.5/dns-query rules: - - GEOSITE,CN,DIRECT + - GEOSITE,category-scholar-!cn,PROXY + - GEOSITE,category-ads-all,REJECT + - GEOSITE,youtube,PROXY + - GEOSITE,google,PROXY + - GEOSITE,cn,DIRECT + - GEOSITE,private,DIRECT + - GEOSITE,steam@cn,DIRECT + - GEOSITE,category-games@cn,DIRECT + - GEOSITE,geolocation-!cn,PROXY + - GEOIP,private,DIRECT,no-resolve + - GEOIP,telegram,PROXY - GEOIP,CN,DIRECT - - MATCH,PROXY \ No newline at end of file + - DST-PORT,80/8080/443/8443,PROXY + - MATCH,DIRECT \ No newline at end of file diff --git a/clashbyte/scheme/_scheme.py b/clashbyte/scheme/_scheme.py index 9f7d6b1..11c6124 100644 --- a/clashbyte/scheme/_scheme.py +++ b/clashbyte/scheme/_scheme.py @@ -9,11 +9,11 @@ from typing import Dict, Any from urllib.parse import ParseResult +path_clash_config = Path(__file__).parent.joinpath("_default.yaml") + @dataclass class Scheme(ABC): - path_clash_config = Path("_default.yaml") - @classmethod @abstractmethod def from_urlparser(cls, parser: ParseResult): diff --git a/clashbyte/scheme/hysteria.py b/clashbyte/scheme/hysteria.py index d33dfaf..30d95b0 100644 --- a/clashbyte/scheme/hysteria.py +++ b/clashbyte/scheme/hysteria.py @@ -28,31 +28,31 @@ class Hysteria(Scheme): downmbps: int # multiport skip (optional) - mport: str = "" + mport: str = None # protocol to use ("udp", "wechat-video", "faketcp") (optional, default: "udp") protocol: Literal["udp", "wechat-video", "faketcp"] = "udp" # authentication payload (string) (optional) - auth: str = "" + auth: str = None # SNI for TLS (optional) - peer: str = "" + peer: str = None # insecure: ignore certificate errors (optional) - insecure: str = "" + insecure: bool = False # QUIC ALPN (optional) alpn: str = "h3" # Obfuscation mode (optional, empty or "xplus") - obfs: Literal["", "xplus"] = "" + obfs: Literal["", "xplus"] = None # Obfuscation password (optional) - obfsParam: str = "" + obfsParam: str = None # remarks alias (optional) - remarks: str = "" + remarks: str = None @classmethod def from_urlparser(cls, parser: ParseResult): @@ -65,7 +65,7 @@ def from_urlparser(cls, parser: ParseResult): :return: """ host, port = parser.netloc.split(":") - data = {"host": host, "port": port, "remarks": parser.fragment} + data = {"host": host, "port": int(port), "remarks": parser.fragment} for e in parser.query.split("&"): k, v = e.split("=") data[k] = v @@ -104,4 +104,5 @@ def to_clash_node(self, **kwargs): } if kwargs: node.update(**kwargs) + node = {k: v for k, v in node.items() if v is not None} return node diff --git a/requirements-dev.txt b/requirements-dev.txt index e5bb3dd..e55729f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,5 @@ loguru>=0.7.0 httpx>=0.24.1 -black -pytest \ No newline at end of file +setuptools>=67.8.0 +pywebio>=1.8.2 +PyYAML>=6.0.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 42083d6..02121e2 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ license="MIT", url="https://github.com/QIN2DIM/ClashByte", packages=find_packages(include=["clashbyte", "clashbyte.*", "LICENSE"], exclude=["tests"]), - install_requires=["loguru>=0.7.0", "httpx>=0.24.1"], + install_requires=["loguru>=0.7.0", "httpx>=0.24.1", "PyYAML>=6.0.1"], extras_require={"dev": ["nox", "pytest"], "test": ["pytest", "black"]}, python_requires=">=3.8", classifiers=[ From 9ff44d6e3d481f0bea88bebd379d562fa9fdc188 Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:34:07 +0800 Subject: [PATCH 4/4] update(dns_query): new example --- .gitignore | 3 ++- examples/dns_query.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/dns_query.py diff --git a/.gitignore b/.gitignore index 4f8324a..9cc8d74 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,5 @@ cython_debug/ .idea/ tests/ clashbyte/database -clashbyte/logs \ No newline at end of file +clashbyte/logs +examples/dns_answers \ No newline at end of file diff --git a/examples/dns_query.py b/examples/dns_query.py new file mode 100644 index 0000000..d8b5d4c --- /dev/null +++ b/examples/dns_query.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Time : 2023/8/9 14:20 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +import json +import os +from pathlib import Path + +from clashbyte.apis import ClashMetaAPI + +# Fill in your Clash.Meta's external control field +os.environ["CLASH_URL"] = "http://127.0.0.1:9090" +os.environ["CLASH_SECRET"] = "" + +answers = Path("dns_answers") +answers.mkdir(exist_ok=True, parents=True) + +for query_domain in [ + "www.baidu.com", + "www.bilibili.com", + "github.com", + "zh.wikipedia.org" +]: + sp = answers.joinpath(f"{query_domain}.json") + result = ClashMetaAPI().dns_query(name=query_domain, dns_type="A") + sp.write_text(json.dumps(result, indent=4))