|
| 1 | +import base64 |
| 2 | +import urllib.request |
| 3 | +import os |
| 4 | +import codecs |
| 5 | +import json |
| 6 | +from qcloud_cos import CosConfig |
| 7 | +from qcloud_cos import CosS3Client |
| 8 | +import sys |
| 9 | +import logging |
| 10 | + |
| 11 | +logging.basicConfig(level=logging.INFO, stream=sys.stdout) |
| 12 | + |
| 13 | + |
| 14 | +def parse(ssr_rul): |
| 15 | + ssr = ssr_rul.strip() |
| 16 | + |
| 17 | + if ssr.startswith('ssr://'): |
| 18 | + base64_encode_str = ssr[6:] |
| 19 | + return parse_ssr(base64_encode_str) |
| 20 | + return "", "" |
| 21 | + |
| 22 | + |
| 23 | +def parse_ssr(base64_encode_str): |
| 24 | + decode_str = base64_decode(base64_encode_str) |
| 25 | + parts = decode_str.split(':') |
| 26 | + if len(parts) != 6: |
| 27 | + print('不能解析SSR链接: %s' % base64_encode_str) |
| 28 | + return "" |
| 29 | + |
| 30 | + server = parts[0] |
| 31 | + port = parts[1] |
| 32 | + protocol = parts[2] |
| 33 | + method = parts[3] |
| 34 | + obfs = parts[4] |
| 35 | + password_and_params = parts[5] |
| 36 | + |
| 37 | + password_and_params = password_and_params.split("/?") |
| 38 | + |
| 39 | + password_encode_str = password_and_params[0] |
| 40 | + password = base64_decode(password_encode_str) |
| 41 | + params = password_and_params[1] |
| 42 | + |
| 43 | + param_parts = params.split('&') |
| 44 | + param_dic = {} |
| 45 | + for part in param_parts: |
| 46 | + key_and_value = part.split('=') |
| 47 | + param_dic[key_and_value[0]] = key_and_value[1] |
| 48 | + |
| 49 | + obfsparam = base64_decode(param_dic['obfsparam']) |
| 50 | + protoparam = base64_decode(param_dic['obfsparam']) |
| 51 | + remarks = base64_decode(param_dic['remarks']) |
| 52 | + group = base64_decode(param_dic['group']) |
| 53 | + # print('server: %s, port: %s, 协议: %s, 加密方法: %s, 密码: %s, 混淆: %s, 混淆参数: %s, 协议参数: %s, 备注: %s, 分组: %s' |
| 54 | + # % (server, port, protocol, method, password, obfs, obfsparam, protoparam, remarks, group)) |
| 55 | + if remarks[:2] in config['china_city_list']: |
| 56 | + remarks = '[国内]' + remarks |
| 57 | + yml = '''- {{ name: "{0}", type: ss, server: {1}, port: {2}, cipher: {3}, password: "{4}" }}''' |
| 58 | + return yml.format(remarks, server, port, method, password), remarks |
| 59 | + |
| 60 | + |
| 61 | +def fill_padding(base64_encode_str): |
| 62 | + need_padding = len(base64_encode_str) % 4 != 0 |
| 63 | + |
| 64 | + if need_padding: |
| 65 | + missing_padding = 4 - need_padding |
| 66 | + base64_encode_str += '=' * missing_padding |
| 67 | + return base64_encode_str |
| 68 | + |
| 69 | + |
| 70 | +def base64_decode(base64_encode_str): |
| 71 | + base64_encode_str = fill_padding(base64_encode_str) |
| 72 | + return base64.urlsafe_b64decode(base64_encode_str).decode('utf-8') |
| 73 | + |
| 74 | + |
| 75 | +def base64_2_str(b64_string): |
| 76 | + b64_string += "=" * ((4 - len(b64_string) % 4) % 4) |
| 77 | + return str(base64.b64decode(b64_string), encoding="utf-8") |
| 78 | + |
| 79 | + |
| 80 | +def update(intput_name, output_name, name_list, yaml_list): |
| 81 | + switcher = False |
| 82 | + rule_switcher = False |
| 83 | + custom_rule_switcher = False |
| 84 | + is_first_line = False |
| 85 | + with codecs.open(intput_name, 'r', encoding='utf-8') as fi, \ |
| 86 | + codecs.open(output_name, 'w', encoding='utf-8') as fo: |
| 87 | + for line in fi: |
| 88 | + if is_first_line: |
| 89 | + # fo.write(update_time + '\n') |
| 90 | + is_first_line = False |
| 91 | + continue |
| 92 | + if line.startswith( |
| 93 | + '''- { name: "Proxy", type: select, proxies:'''): |
| 94 | + proxy_list = list() |
| 95 | + proxy_list.append("auto") |
| 96 | + for name in name_list: |
| 97 | + if ('倍率:0.' not in name |
| 98 | + and not name.endswith('(倍率:1)')) or '[国内]' in name: |
| 99 | + proxy_list.append(name) |
| 100 | + newline = '''- {{ name: "Proxy", type: select, proxies: {} }}\n'''.format( |
| 101 | + proxy_list) |
| 102 | + fo.write(newline.replace("'", '"')) |
| 103 | + continue |
| 104 | + if line.startswith( |
| 105 | + '''- { name: "auto", type: url-test, proxies:'''): |
| 106 | + url_test_list = config['append_name'] |
| 107 | + for name in name_list: |
| 108 | + if ('倍率:0.' in name or name.endswith('(倍率:1.') |
| 109 | + or '(倍率:2' in name |
| 110 | + ) or '(倍率:2' in name and '[国内]' not in name: |
| 111 | + url_test_list.append(name) |
| 112 | + newline = '''- {{ name: "auto", type: url-test, proxies: {}, url: "http://www.gstatic.com/generate_204", interval: 300 }}\n'''.format( |
| 113 | + url_test_list) |
| 114 | + fo.write(newline.replace("'", '"')) |
| 115 | + continue |
| 116 | + if line == "## auto changed by py3 begin ##\n": |
| 117 | + switcher = True |
| 118 | + fo.write(line) |
| 119 | + if line == "Rule:\n": |
| 120 | + rule_switcher = True |
| 121 | + fo.write(rule_str_part_1) |
| 122 | + if rule_switcher and line == "# 自定义规则\n": |
| 123 | + custom_rule_switcher = True |
| 124 | + if line == "## 您可以在此处插入您补充的自定义规则\n": |
| 125 | + custom_rule_switcher = False |
| 126 | + fo.write(line) |
| 127 | + fo.write(rule_str_part_2) |
| 128 | + if rule_switcher and custom_rule_switcher: |
| 129 | + fo.write(line) |
| 130 | + if line == "## auto changed by py3 end ##\n": |
| 131 | + switcher = False |
| 132 | + new_line = "" |
| 133 | + for ss in yaml_list: |
| 134 | + new_line = new_line + ss + '\n' |
| 135 | + fo.write(new_line) |
| 136 | + if not switcher and not rule_switcher: |
| 137 | + fo.write(line) |
| 138 | + |
| 139 | + |
| 140 | +def init_COS(config): |
| 141 | + secret_id = config['cos_secret_id'] # 替换为用户的 secretId |
| 142 | + secret_key = config['cos_secret_key'] # 替换为用户的 secretKey |
| 143 | + region = config['cos_region'] # 替换为用户的 Region |
| 144 | + token = None # 使用临时密钥需要传入 Token,默认为空,可不填 |
| 145 | + scheme = 'https' # 指定使用 http/https 协议来访问 COS,默认为 https,可不填 |
| 146 | + config = CosConfig(Region=region, SecretId=secret_id, |
| 147 | + SecretKey=secret_key, Token=token, Scheme=scheme) |
| 148 | + # 2. 获取客户端对象 |
| 149 | + client = CosS3Client(config) |
| 150 | + return client |
| 151 | + |
| 152 | + |
| 153 | +def download_template(config, client): |
| 154 | + # 获取文件到本地 |
| 155 | + response = client.get_object( |
| 156 | + Bucket=config['cos_bucket'], |
| 157 | + Key=config['input_name'], |
| 158 | + ) |
| 159 | + response['Body'].get_stream_to_file(config['input_name']) |
| 160 | + |
| 161 | + |
| 162 | +def get_config(): |
| 163 | + f = open('config.json', 'r') |
| 164 | + text = f.read() |
| 165 | + f.close() |
| 166 | + config = json.loads(text) |
| 167 | + return config |
| 168 | + |
| 169 | + |
| 170 | +def upload_file(client, config): |
| 171 | + # 根据文件大小自动选择简单上传或分块上传,分块上传具备断点续传功能。 |
| 172 | + response = client.upload_file( |
| 173 | + Bucket=config['cos_bucket'], |
| 174 | + LocalFilePath=config['output_name'], |
| 175 | + Key=config['upload_key'], |
| 176 | + PartSize=1, |
| 177 | + MAXThread=10, |
| 178 | + EnableMD5=False |
| 179 | + ) |
| 180 | + print(response['ETag']) |
| 181 | + |
| 182 | + |
| 183 | +if __name__ == '__main__': |
| 184 | + config = get_config() |
| 185 | + client = init_COS(config) |
| 186 | + download_template(config, client) |
| 187 | + |
| 188 | + # 更新 cordcould 规则 ,同时更新 https://github.com/Hackl0us/SS-Rule-Snippet 规则 |
| 189 | + rule_url = config['rule_url'] |
| 190 | + rule_f = urllib.request.urlopen(rule_url) |
| 191 | + rule_raw = rule_f.read().decode('utf-8') |
| 192 | + rule_str = rule_raw[rule_raw.find('Rule:\n'):] |
| 193 | + rule_str_part_1 = rule_str[:rule_str.find('# 自定义规则\n')] |
| 194 | + rule_str_part_2 = rule_str[rule_str.find('## 您可以在此处插入您补充的自定义规则\n') + |
| 195 | + len("## 您可以在此处插入您补充的自定义规则\n"):] |
| 196 | + update_time = rule_raw[:rule_raw.find('\n')] |
| 197 | + url = config['url'] |
| 198 | + yaml_list = list() |
| 199 | + headers = { |
| 200 | + 'User-Agent': |
| 201 | + 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6' |
| 202 | + } |
| 203 | + req = urllib.request.Request(url=url, headers=headers) |
| 204 | + f = urllib.request.urlopen(req) |
| 205 | + b64_string = f.read().decode('utf-8') |
| 206 | + result = base64_2_str(b64_string) |
| 207 | + |
| 208 | + ssr_url_list = result.split("\n") |
| 209 | + name_list = list() |
| 210 | + name_list.extend(config['append_name']) |
| 211 | + for ssr_url in ssr_url_list: |
| 212 | + tmp, name = parse(ssr_url) |
| 213 | + if len(tmp) > 0: |
| 214 | + yaml_list.append(tmp) |
| 215 | + name_list.append(name) |
| 216 | + |
| 217 | + input_name = config['input_name'] |
| 218 | + output_name = config['output_name'] |
| 219 | + update(input_name, output_name, name_list, yaml_list) |
| 220 | + upload_file(client, config) |
0 commit comments