├── .DS_Store ├── setup.py ├── README.md ├── .gitignore ├── config.py ├── getrules.py ├── getnodes.py ├── main.py ├── functions.py └── proxygroup.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WithdewHua/ConfigSplicing/HEAD/.DS_Store -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # 3 | 4 | from setuptools import setup 5 | 6 | setup( 7 | name='ConfigSplicing', 8 | version='0.4', 9 | py_modules=['main', 'getnodes', 'getrules', 'functions', 'config', 'proxygroup'], 10 | install_requires=[ 11 | 'Click', 12 | 'ruamel.yaml', 13 | 'wget' 14 | ], 15 | entry_points=''' 16 | [console_scripts] 17 | cs=main:main 18 | ''', 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目简介 2 | 3 | 用来生成 Surge/Clash 配置文件的 `python` 脚本。 4 | 5 | # 实现功能 6 | 7 | - [x] 支持不同类型的节点订阅链接(Surge、SS) 8 | - [x] 支持同时输入多个节点订阅链接(相同类型或者不同类型) 9 | - [x] 支持自定义规则链接 10 | - [x] 支持为策略组添加节点 11 | - [x] 支持修改策略组名字(有限) 12 | - [x] 支持输出 Surge 或者 Clash 配置文件 13 | 14 | # 使用方法 15 | 16 | - 下载或者克隆项目后,进入项目文件夹: 17 | 18 | ```bash 19 | # 例如 20 | cd ConfigSplicing 21 | ``` 22 | 23 | - 安装依赖: 24 | 25 | ```bash 26 | pip3 install . 27 | ``` 28 | 29 | - 输入命令: 30 | 31 | ```bash 32 | # 查看帮助 33 | cs --help 34 | 35 | Usage: cs [OPTIONS] [SUB_LINKS]... 36 | 37 | ConfigSplicing 38 | 39 | Options: 40 | -R, --rename 更改策略组名 41 | -c, --clash 输出Clash配置文件 42 | -r, --rule TEXT 规则链接(默认为神机规则) 43 | -i, --interval INTEGER 设置延迟组的间隔时间(默认1200) 44 | --help Show this message and exit. 45 | 46 | 47 | # 示例 48 | ## 不自定义规则链接 49 | cs ss_link1 ss_link2 50 | ## 自定义规则链接 51 | cs -r rule_link ss_link1 ss_link2 52 | ## 输出为 clash 配置文件 53 | cs -r rule_link ss_link -c 54 | ## 输出为 clash 配置文件,并且需要更改策略组名字 55 | cs ss_link1 ss_link2 -c -R 56 | ``` 57 | 58 | - 然后根据提示做完就可以了,能不能成功就随缘吧。:relieved: -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | 110 | # generated files 111 | *.conf 112 | /results/*.yaml 113 | 114 | # IDE 115 | .idea/ -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # Copyright: WithdewHua 3 | # 2019-09-14 4 | # 5 | 6 | import os 7 | from urllib import request 8 | 9 | from ruamel import yaml 10 | 11 | 12 | def read_config(urls): 13 | """ 14 | 读取节点或者规则链接 15 | :param urls: 节点链接或者规则链接 16 | :return: 读取的节点或者规则配置 17 | """ 18 | 19 | # 修改 headers 20 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 21 | 'Chrome/75.0.3770.142 Safari/537.36'} 22 | 23 | # 存放最后读取的配置文件 24 | pages = [] 25 | 26 | # 判断链接为元组还是字符串 27 | if type(urls) == str: 28 | # 若为字符串(规则链接) 29 | page1 = request.Request(urls, headers=headers) 30 | page2 = request.urlopen(page1) 31 | pages = page2.readlines() 32 | 33 | if type(urls) == tuple: 34 | # 若为元组(节点链接) 35 | for url in urls: 36 | # 正常获取每个 URL 内容 37 | page1 = request.Request(url, headers=headers) 38 | page2 = request.urlopen(page1) 39 | # 将各个URL内容拼接在一个字符串数组中 40 | pages += page2.readlines() 41 | return pages 42 | 43 | 44 | def write_surge_config(new_config): 45 | """ 46 | 将配置写入文件中 47 | :param new_config: 拼接后的配置文件 48 | :return: 49 | """ 50 | 51 | # 写入文件准备 52 | filename = input('为生成的文件取个好听的名字吧(默认 WithdewHua):').replace(' ', '') 53 | if filename == '': 54 | filename = 'WithdewHua' 55 | if not os.path.exists('results'): 56 | os.mkdir('results') 57 | path = 'results/' + filename + '.conf' 58 | 59 | with open(path, mode='w', encoding='utf-8') as f: 60 | for line in new_config: 61 | f.write(line) 62 | 63 | 64 | def write_clash_config(proxy, proxy_group, rule, clash_yaml): 65 | """ 66 | 生成 clash 的配置文件 67 | :param rule: 规则列表 68 | :param proxy: 节点列表 69 | :param proxy_group: 策略组列表 70 | :param clash_yaml: 原规则文件 71 | :return: 72 | """ 73 | 74 | clash_yaml.update({'Proxy': proxy, 'Proxy Group': proxy_group, 'Rule': rule}) 75 | 76 | # 写入文件准备 77 | filename = input('为生成的文件取个好听的名字吧(默认 WithdewHua):').replace(' ', '') 78 | if filename == '': 79 | filename = 'WithdewHua' 80 | if not os.path.exists('results'): 81 | os.mkdir('results') 82 | path = 'results/' + filename + '.yaml' 83 | 84 | with open(path, 'w', encoding='utf-8') as f: 85 | yaml.dump(clash_yaml, f, Dumper=yaml.RoundTripDumper, allow_unicode=True) 86 | -------------------------------------------------------------------------------- /getrules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Copyright: WithdewHua 4 | # 2019-08-05 5 | 6 | import re 7 | import os 8 | import wget 9 | from config import read_config 10 | from ruamel import yaml 11 | 12 | 13 | class GetRules: 14 | """ 15 | 获取规则 16 | """ 17 | 18 | def __init__(self, url): 19 | """ 20 | 初始化 21 | :param url: 规则链接 22 | """ 23 | 24 | self.url = url 25 | # 获取整个文件内容并存入字符串数组中 26 | self.config = read_config(self.url) 27 | self.surge_rules = [] 28 | self.clash_rules = {} 29 | 30 | def surge(self): 31 | """获取 Surge 格式的规则""" 32 | 33 | # 存到规则列表中 34 | for rule in self.config: 35 | rule = rule.decode('utf-8') 36 | self.surge_rules.append(rule) 37 | 38 | # 获取各个关键字段的数组索引值 39 | for rule in self.surge_rules: 40 | if re.search(r'\[General\]', rule): 41 | general_start = self.surge_rules.index(rule) 42 | if re.search(r'\[Proxy\]', rule): 43 | proxy_start = self.surge_rules.index(rule) 44 | if re.search(r'\[Proxy Group\]', rule): 45 | group_start = self.surge_rules.index(rule) 46 | if re.search(r'\[Rule\]', rule): 47 | rule_start = self.surge_rules.index(rule) 48 | 49 | # 生成各部分字段数组 50 | general = self.surge_rules[general_start:proxy_start] 51 | group = self.surge_rules[group_start:rule_start] 52 | rule = self.surge_rules[rule_start:] 53 | 54 | # 获取策略组名字和策略 55 | group_names = [] 56 | for li in group: 57 | res = re.search(r'(.+)=\s+(select|url-test|fallback)', li) 58 | res2 = re.search(r'(#.+)', li) 59 | # 只获取非注释的策略组 60 | if (res is not None) and (res2 is None): 61 | group_names.append(res.group(1).strip()) 62 | return general, group_names, rule 63 | 64 | def clash(self): 65 | """ 66 | 获取 Clash 格式的规则 67 | :return: 68 | """ 69 | 70 | # 下载规则文件 71 | out_file = 'config.yaml' 72 | wget.download(self.url, out=out_file) 73 | # 读取规则文件 74 | with open(out_file, 'r', encoding='utf-8') as f: 75 | self.clash_rules = yaml.load(f, Loader=yaml.RoundTripLoader) 76 | # 读取对象后删除下载文件 77 | os.remove(out_file) 78 | return self.clash_rules 79 | -------------------------------------------------------------------------------- /getnodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # Copyright: WithdewHua 3 | # 2019-08-09 4 | 5 | import base64 6 | import re 7 | 8 | from config import read_config 9 | import functions as fun 10 | 11 | 12 | class GetNodes: 13 | """ 14 | 获取节点 15 | """ 16 | 17 | def __init__(self, urls): 18 | """ 19 | 获取节点链接里的所有配置 20 | :param urls: 节点链接 21 | """ 22 | 23 | # 获取整个文件内容并存入字符串数组中 24 | self.config = read_config(urls) 25 | # 初始化节点和节点名列表 26 | self.nodes = ['[Proxy]\n'] 27 | self.node_names = [] 28 | self.nodes_dict = {} 29 | 30 | def get_ss(self): 31 | """ 32 | 从订阅链接或者托管链接获取节点 33 | :return: 节点字典 34 | """ 35 | 36 | # 初始化 37 | _nodes = [] 38 | _nodes_list = [] 39 | 40 | # 遍历获取的节点配置,分离出 Surge 格式和 ss 格式 41 | for line in self.config: 42 | line = line.decode('utf-8').strip() 43 | # 匹配 Surge 格式节点 44 | res1 = re.search(r'(.*)=\s*(custom|shadowsocks)', line) 45 | # 匹配 ss base64 编码内容 46 | res2 = re.search(r'^[A-Za-z0-9+/]{8,}={0,2}$', line) 47 | if res1: 48 | # 对于 surge 格式来说直接添加即可 49 | self.nodes.append(line + '\n') 50 | self.node_names.append(res1.group(1)) 51 | if res2: 52 | # 判断编码是否需要补充字符 53 | missing = len(line) % 4 54 | if missing != 0: 55 | line += '=' * (4 - missing) 56 | # 将 base64 编码内容解码并转成字符串列表 57 | _nodes.append(base64.b64decode(line.encode()).decode().strip()) 58 | _nodes_list += _nodes[-1].split('\n') 59 | 60 | # 处理字符串列表里面的 ss uri 61 | for node in _nodes_list: 62 | # 解析每个 ss uri 63 | # ss_params: [服务器、端口、加密方式、密码] 64 | node_name, ss_params = fun.parse_ss(node) 65 | # 存入节点字典中,key:节点名;value:节点参数 66 | self.nodes_dict[node_name] = ss_params 67 | 68 | def surge_nodes(self): 69 | """ 70 | 构造 Surge 格式的节点列表 71 | :return: Surge 格式的节点列表和节点名列表 72 | """ 73 | 74 | self.get_ss() 75 | 76 | for key, value in self.nodes_dict.items(): 77 | join = key + ' = custom' + ', ' + ', '.join(value) + ', udp-relay=true, tfo=true' + '\n' 78 | # 添加到节点列表 79 | self.nodes.append(join) 80 | self.node_names.append(key) 81 | return self.nodes, self.node_names 82 | 83 | def clash_nodes(self, clash_file): 84 | """ 85 | 构造 clash 格式的节点 86 | :return: 87 | """ 88 | 89 | self.get_ss() 90 | 91 | # 初始化 92 | # clash 节点列表 93 | self.nodes = clash_file['Proxy'] 94 | # 删除原文件中的示例节点 95 | self.nodes.clear() 96 | for key, value in self.nodes_dict.items(): 97 | node = {} 98 | node.update(dict(name=key, type='ss', server=value[0], port=int(value[1]), cipher=value[2], password=value[3], 99 | udp=True)) 100 | self.nodes.append(node) 101 | self.node_names.append(key) 102 | return self.nodes, self.node_names 103 | 104 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*-coding:utf-8 -*- 3 | # 4 | # Copyright:WithdewHua 5 | # modified: 2019-08-07 6 | 7 | import click 8 | 9 | from functions import get_group_name_from_clash, change_rule_policy 10 | from config import write_surge_config, write_clash_config 11 | from getnodes import GetNodes 12 | from getrules import GetRules 13 | from proxygroup import get_proxy_group 14 | 15 | 16 | @click.command() 17 | @click.option('-R', '--rename', is_flag=True, help='更改策略组名') 18 | @click.option('-c', '--clash', is_flag=True, help='输出Clash配置文件') 19 | @click.option('-r', '--rule', help='规则链接(默认为神机规则)') 20 | @click.option('-i', '--interval', default=1200, help='设置延迟组的间隔时间(默认1200)') 21 | @click.argument('sub_links', nargs=-1) 22 | def main(sub_links, clash, rename, interval, rule=None): 23 | """ 24 | ConfigSplicing 25 | """ 26 | 27 | try: 28 | # Clash 配置文件 29 | if clash: 30 | # 采用默认规则链接 31 | if not rule: 32 | rule = 'https://raw.githubusercontent.com/ConnersHua/Profiles/master/Clash/Pro.yaml' 33 | # 处理托管链接和规则链接 34 | # 处理规则 35 | print('规则获取中...') 36 | # 获取 clash 格式规则 37 | clash_file = GetRules(rule).clash() 38 | print('节点获取中...') 39 | # 获取节点和节点名字 40 | nodes, node_names = GetNodes(sub_links).clash_nodes(clash_file) 41 | # 获取策略组名字 42 | group_names = get_group_name_from_clash(clash_file) 43 | # 询问各个策略组的节点选择 44 | groups, changed_group_dict = get_proxy_group(node_names, group_names, interval, rename) 45 | 46 | # 更名 47 | if rename: 48 | rules = change_rule_policy(changed_group_dict, clash_file['Rule']) 49 | else: 50 | rules = clash_file['Rule'] 51 | 52 | # 写入文件 53 | write_clash_config(nodes, groups, rules, clash_file) 54 | 55 | # Surge 配置文件 56 | else: 57 | if not rule: 58 | rule = 'https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Pro.conf' 59 | # 处理托管链接和规则链接 60 | print('节点获取中...') 61 | # 获取节点和节点名字 62 | nodes, node_names = GetNodes(sub_links).surge_nodes() 63 | # 处理规则 64 | print('规则获取中...') 65 | # 获取 Surge 格式规则 66 | general, group_names, rules = GetRules(rule).surge() 67 | 68 | # 询问各个策略组的节点选择 69 | group_list, changed_group_dict = get_proxy_group(node_names, group_names, interval, rename) 70 | 71 | # 拼接出新的 Proxy Group 列表 72 | proxy_group = ['\n[Proxy Group]\n'] 73 | for group_dict in group_list: 74 | group = group_dict['name'] + ' = ' + group_dict['type'] + ', ' + ', '.join(group_dict['proxies']) + '\n' 75 | proxy_group.append(group) 76 | proxy_group.append('\n') 77 | 78 | # 更名 79 | if rename: 80 | rules = change_rule_policy(changed_group_dict, rules) 81 | # 生成新的配置 82 | new_config = general + nodes + proxy_group + rules 83 | 84 | # 写入文件 85 | write_surge_config(new_config) 86 | 87 | # 结束提示 88 | print('脚本运行结束,请在 results 文件夹查看文件!') 89 | 90 | except (ImportError, EOFError, InterruptedError, KeyboardInterrupt) as e: 91 | print('\n' + '程序异常退出! %s' % e) 92 | 93 | 94 | if __name__ == '__main__': 95 | main() 96 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # Copyright: WithdewHua 3 | # 2019-08-08 4 | 5 | import base64 6 | import urllib.parse 7 | 8 | 9 | def parse_ss(uri): 10 | """ 11 | 解析 ss URI 12 | :param uri: ss uri 链接 13 | :return: 14 | """ 15 | 16 | # 格式一:只 base64 编码加密和密码 17 | if '@' in uri: 18 | base64_encode_str = uri[5:uri.find('@')] 19 | # 判断是否需要补充字符 20 | missing = len(base64_encode_str) % 4 21 | if missing != 0: 22 | base64_encode_str += '=' * (4 - missing) 23 | server_and_port = uri[(uri.find('@') + 1):uri.find('#')] 24 | node_name_str = uri[(uri.find('#') + 1):] 25 | 26 | # 将节点名字用 unquotes 解码出来 27 | node_name = urllib.parse.unquote(node_name_str).strip() 28 | # base64 解码 29 | method_and_password = base64.b64decode(base64_encode_str).decode('utf-8') 30 | 31 | # 将 method password server port 放入一个列表中 32 | list1 = method_and_password.split(':') 33 | list2 = server_and_port.split(':') 34 | ss_params = list2 + list1 35 | 36 | # 格式二:将加密、密码、服务器、端口全部进行 base64 编码 37 | else: 38 | base64_encode_str = uri[5:uri.find('#')] 39 | # 判断是否需要补充字符 40 | missing = len(base64_encode_str) % 4 41 | if missing != 0: 42 | base64_encode_str += '=' * (4 - missing) 43 | 44 | # 解码,以 # 为分界点,后面的即为节点名字 45 | node_name = urllib.parse.unquote(uri[(uri.find('#')) + 1:]).strip() 46 | # 处理节点的各个参数 47 | ss_params_tmp = base64.b64decode(base64_encode_str).decode('utf-8').split(':') 48 | if len(ss_params_tmp) != 3: 49 | print('链接有误!请检查链接') 50 | return 51 | else: 52 | password_and_server = ss_params_tmp[1].split('@') 53 | ss_params = [password_and_server[1], ss_params_tmp[2], ss_params_tmp[0], password_and_server[0]] 54 | return node_name, ss_params 55 | 56 | 57 | def input_new_group_name(group_names): 58 | """ 59 | 修改策略组名字 60 | :param group_names: 原策略组名字 61 | :return: 新旧策略组名字字典 62 | """ 63 | 64 | group_name_changed_dict = {} 65 | new_group_name = list(group_names) 66 | print('原策略组名字分别为:') 67 | for i, el in enumerate(group_names): 68 | print(i, el) 69 | while True: 70 | str1 = input('请选择需要更名的策略组(回车退出):').strip() 71 | if str1 == '': 72 | break 73 | else: 74 | try: 75 | index = int(str1) 76 | except ValueError: 77 | print('请输入正确的序号') 78 | continue 79 | else: 80 | if index > len(group_names) - 1: 81 | print('序号输入错误!不能超过策略组个数!') 82 | else: 83 | str2 = input('请输入新名字:').strip() 84 | group_name_changed_dict[group_names[index]] = str2 85 | new_group_name[index] = str2 86 | continue 87 | 88 | return new_group_name, group_name_changed_dict 89 | 90 | 91 | def get_group_name_from_clash(clash_yaml): 92 | """ 93 | 获取 clash 策略组名 94 | :param clash_yaml: clash 规则文件 95 | :return: 策略组名列表 96 | """ 97 | 98 | proxy_group = clash_yaml['Proxy Group'] 99 | group_names = [] 100 | for group in proxy_group: 101 | name = group['name'] 102 | group_names.append(name) 103 | return group_names 104 | 105 | 106 | def change_rule_policy(changed_group_dict, rule_list): 107 | """ 108 | 为配置中的规则更改策略组名字 109 | :param changed_group_dict: 新旧策略名列表 110 | :param rule_list: 配置文件中的规则字段列表 111 | :return: 更改策略名后的规则字段列表 112 | """ 113 | 114 | new_rules = [] 115 | for rule in rule_list: 116 | for key, value in changed_group_dict.items(): 117 | rule = rule.replace(key, value) 118 | new_rules.append(rule) 119 | return new_rules 120 | -------------------------------------------------------------------------------- /proxygroup.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # Copyright: WithdewHua 3 | # 2019-09-14 4 | # 5 | 6 | from functions import input_new_group_name 7 | 8 | 9 | class ProxyGroup: 10 | """ 11 | 策略组处理 12 | """ 13 | 14 | def __init__(self, nodes_name, groups_name, interval): 15 | """ 16 | 初始化 17 | :param nodes_name: 节点名列表 18 | :param groups_name: 策略组名列表 19 | :return: 20 | """ 21 | 22 | self.nodes_list = nodes_name 23 | self.groups_name = groups_name 24 | # 策略类型列表 25 | self.type_list = ['select', 'url-test', 'fallback', 'load-balance'] 26 | # 可用参数列表 27 | self.params_dict = dict(url='http://www.gstatic.com/generate_204', interval=interval) 28 | self.chosen_dict = dict(name='', type='', proxies=[]) 29 | self.chosen_list = [] 30 | 31 | def choose_type(self, group_name): 32 | """ 33 | 选择策略组的策略方式 34 | :param group_name: 策略名 35 | :return: 36 | """ 37 | # 展示所有可供选择的内容 38 | print('可供策略组 ' + group_name + ' 选择的策略方式有:') 39 | for i, el in enumerate(self.type_list): 40 | print(i, el) 41 | while True: 42 | # 检查输入是否为数字 43 | try: 44 | num = int(input('请输入您的选择:')) 45 | except ValueError: 46 | print('请输入数字!') 47 | continue 48 | else: 49 | # 检查输入是否超过序号值 50 | try: 51 | self.chosen_dict.update(dict(name=group_name, type=self.type_list[num])) 52 | except IndexError: 53 | print('请输入正确的序号!') 54 | continue 55 | else: 56 | break 57 | 58 | def choose_policy(self, group_name): 59 | """ 60 | 选择可用的策略组 61 | :param group_name: 策略组名字 62 | :return: 63 | """ 64 | 65 | avail_group = list(self.groups_name) 66 | avail_group.remove(group_name) 67 | avail_group.extend(['DIRECT', 'REJECT']) 68 | # 展示该策略可用的策略名 69 | print('可供策略组 ' + group_name + ' 引用的策略组有:') 70 | for i, el in enumerate(avail_group): 71 | print(i, el) 72 | while True: 73 | # 检查输入是否为数字 74 | try: 75 | str_list = input('请输入您的选择(回车跳过):').split() 76 | for num in str_list: 77 | num = int(num) 78 | try: 79 | self.chosen_dict['proxies'].append(avail_group[num]) 80 | except IndexError: 81 | print('请输入正确的序号!') 82 | continue 83 | else: 84 | continue 85 | 86 | except ValueError: 87 | print('请输入数字!') 88 | continue 89 | else: 90 | break 91 | 92 | def choose_node(self, group_name): 93 | """ 94 | 选择策略组的 95 | :param group_name: 96 | :return: 97 | """ 98 | 99 | # 展示所有可选的节点 100 | print('可供策略组 ' + group_name + ' 选择的节点有:') 101 | for i, el in enumerate(self.nodes_list): 102 | print(i, el) 103 | while True: 104 | # 检查输入是否为数字 105 | try: 106 | str_list = input('请输入您的选择(回车不选,666 全选):').split() 107 | # 如果为空代表不选 108 | if not str_list: 109 | break 110 | # 666 全选 111 | if len(str_list) == 1 and int(str_list[0]) == 666: 112 | self.chosen_dict['proxies'].extend(self.nodes_list) 113 | # 其他值则选定了具体的节点 114 | else: 115 | for num in str_list: 116 | num = int(num) 117 | try: 118 | self.chosen_dict['proxies'].append(self.nodes_list[num]) 119 | except IndexError: 120 | print('请输入正确的序号!') 121 | break 122 | else: 123 | continue 124 | 125 | except ValueError: 126 | print('请输入数字!') 127 | continue 128 | else: 129 | break 130 | 131 | def add_params(self): 132 | """ 133 | 选择一些可用参数 134 | :return: 135 | """ 136 | 137 | if self.chosen_dict['type'] in ['url-test', 'fallback', 'load-balance']: 138 | self.chosen_dict.update(self.params_dict) 139 | 140 | 141 | def get_proxy_group(nodes_name, groups_name, interval, rename): 142 | """ 143 | 构造出新的 proxy group 144 | :return: 构造好的 proxy group 列表 145 | """ 146 | 147 | pg = ProxyGroup(nodes_name, groups_name, interval) 148 | 149 | if rename: 150 | pg.groups_name, changed_group_dict = input_new_group_name(groups_name) 151 | else: 152 | changed_group_dict = {} 153 | 154 | for group_name in pg.groups_name: 155 | pg.chosen_dict = dict(name='', type='', proxies=[]) 156 | pg.choose_type(group_name) 157 | pg.choose_policy(group_name) 158 | pg.choose_node(group_name) 159 | pg.add_params() 160 | pg.chosen_list.append(pg.chosen_dict) 161 | 162 | return pg.chosen_list, changed_group_dict 163 | --------------------------------------------------------------------------------