├── models ├── charsets.json ├── pikpak3.0.onnx └── pikpak4.0.onnx ├── requirements.txt ├── .github └── workflows │ └── run.yml ├── image.py ├── recognize.py └── run.py /models/charsets.json: -------------------------------------------------------------------------------- 1 | {"charset": ["wrong", "correct"], "image": [-1, 128], "word": true, "channel": 3} -------------------------------------------------------------------------------- /models/pikpak3.0.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarisaIsNoob/pikpak_auto_invite2/HEAD/models/pikpak3.0.onnx -------------------------------------------------------------------------------- /models/pikpak4.0.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarisaIsNoob/pikpak_auto_invite2/HEAD/models/pikpak4.0.onnx -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.4 2 | ddddocr==1.4.10 3 | matplotlib==3.7.1 4 | numpy==1.23.3 5 | opencv_python==4.6.0.66 6 | opencv_python_headless==4.6.0.66 7 | rich==13.7.1 8 | -------------------------------------------------------------------------------- /.github/workflows/run.yml: -------------------------------------------------------------------------------- 1 | name: pikpak_auto_invite 2 | on: 3 | push: 4 | branches: [ main ] 5 | paths: 6 | - '**' 7 | - '!.gitignore' 8 | - '!README.md' 9 | schedule: 10 | # 每天晚六点十分执行一次 11 | - cron: '10 10 * * *' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Set Time 18 | run: sudo timedatectl set-timezone 'Asia/Shanghai' 19 | - uses: actions/checkout@v2 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.9' 23 | - run: pip install -r requirements.txt 24 | - name: Run 25 | env: 26 | INVITE_CODE: ${{secrets.INVITE_CODE}} 27 | PUSHPLUS_TOKEN: ${{secrets.PUSHPLUS_TOKEN}} 28 | run: python run.py 29 | -------------------------------------------------------------------------------- /image.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | 6 | 7 | # 导入原始图像 8 | def read_img(path): 9 | img = cv2.imread(path) 10 | # 获取图像基本信息 11 | height, width, channel = img.shape 12 | return img, height, width 13 | 14 | 15 | # 图像切割 16 | def cut_img(img, height, width, matrix): 17 | # 切割图像 18 | img_list = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix[0]))] 19 | for i in range(len(matrix[0])): 20 | for j in range(len(matrix[0])): 21 | # 纵向切割 22 | img_list[j][i] = img[i * height // len(matrix[0]): (i + 1) * height // len(matrix[0]), 23 | j * width // len(matrix[0]): (j + 1) * width // len(matrix[0])] 24 | return img_list 25 | 26 | 27 | # 图像重组 28 | def re_img(img_list, height, width, matrix): 29 | new_img_list = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix[0]))] 30 | for i in range(len(matrix[0])): 31 | for j in range(len(matrix[0])): 32 | k, l = matrix[i][j].split(',') 33 | new_img_list[i][j] = img_list[int(k)][int(l)] 34 | # 图像拼接 35 | img = np.zeros((height, width, 3), np.uint8) 36 | for i in range(len(matrix[0])): 37 | for j in range(len(matrix[0])): 38 | img[i * height // len(matrix[0]): (i + 1) * height // len(matrix[0]), 39 | j * width // len(matrix[0]): (j + 1) * width // len(matrix[0])] = new_img_list[i][j] 40 | return img 41 | 42 | 43 | # 图像展示 44 | def show_img_list(img_list): 45 | n = len(img_list) 46 | for i in range(n): 47 | for j in range(n): 48 | plt.subplot(n, n, i * n + j + 1) 49 | plt.imshow(img_list[i][j]) 50 | plt.axis('off') 51 | plt.show() 52 | 53 | 54 | def show_img(img): 55 | plt.imshow(img) 56 | plt.axis('off') 57 | plt.show() 58 | 59 | 60 | # 保存图像 61 | def save_img(img, path): 62 | if not os.path.exists(os.path.dirname(path)): 63 | os.makedirs(os.path.dirname(path)) 64 | cv2.imwrite(path, img) 65 | 66 | 67 | # 删除缓存图像 68 | def delete_img(): 69 | for file in os.listdir('temp/'): 70 | os.remove(f'temp/{file}') 71 | # print('删除缓存图片成功') 72 | 73 | 74 | def run(img_path, frames): 75 | iamge, height, width = read_img(img_path) 76 | img_list = cut_img(iamge, height, width, frames[0]['matrix']) 77 | for i in range(len(frames)): 78 | matrix = frames[i]['matrix'] 79 | temp_img = re_img(img_list, height, width, matrix) 80 | save_img(temp_img, f'temp/{i}.png') 81 | -------------------------------------------------------------------------------- /recognize.py: -------------------------------------------------------------------------------- 1 | import ddddocr 2 | import os 3 | from PIL import Image 4 | import numpy as np 5 | import os 6 | import cv2 7 | 8 | """ 9 | by lingdanqing 10 | 11 | https://github.com/lingdanqing 12 | """ 13 | # 去除白边的函数 14 | def remove_white_borders(image): 15 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 16 | _, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY) 17 | thresh = cv2.bitwise_not(thresh) 18 | contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 19 | if contours: 20 | c = max(contours, key=cv2.contourArea) 21 | x, y, w, h = cv2.boundingRect(c) 22 | cropped_image = image[y:y + h, x:x + w] 23 | return cropped_image 24 | return image 25 | 26 | 27 | # 分割图片 28 | def split_image(image, rows, cols, save_path, image_name): 29 | height, width, _ = image.shape 30 | tile_height = height // rows 31 | tile_width = width // cols 32 | tiles = [] 33 | if not os.path.exists(save_path): 34 | os.makedirs(save_path) 35 | for row in range(rows): 36 | for col in range(cols): 37 | tile = image[row * tile_height:(row + 1) * tile_height, col * tile_width:(col + 1) * tile_width] 38 | tile = remove_white_borders(tile) # 去除白边 39 | tile = cv2.resize(tile, (tile_width, tile_height)) 40 | tiles.append(tile) 41 | 42 | # 保存切割图 43 | # tile_image = Image.fromarray(tile) 44 | # tile_image.save(os.path.join(save_path, f"{image_name}_tile_{row}_{col}.png")) 45 | return tiles 46 | 47 | 48 | # 计算相邻块边缘差异 49 | def calculate_edge_difference(tiles, rows, cols): 50 | total_diff = 0 51 | for row in range(rows): 52 | for col in range(cols - 1): 53 | right_edge = tiles[row * cols + col][:, -1, :] 54 | left_edge = tiles[row * cols + col + 1][:, 0, :] 55 | total_diff += np.sum((right_edge - left_edge) ** 2) 56 | for col in range(cols): 57 | for row in range(rows - 1): 58 | bottom_edge = tiles[row * cols + col][-1, :, :] 59 | top_edge = tiles[(row + 1) * cols + col][0, :, :] 60 | total_diff += np.sum((bottom_edge - top_edge) ** 2) 61 | return total_diff 62 | 63 | 64 | def process_images_in_folder2(folder_path): 65 | image_files = [f for f in os.listdir(folder_path) if f.endswith(('.png', '.jpg', '.jpeg'))] 66 | edge_diffs = [] 67 | for image_file in image_files: 68 | image_path = os.path.join(folder_path, image_file) 69 | image = Image.open(image_path) 70 | 71 | image_np = np.array(image) 72 | tiles = split_image(image_np, 4, 4, 'out', image_file.split('.')[0]) 73 | edge_diff = calculate_edge_difference(tiles, 4, 4) 74 | edge_diffs.append((image_file, edge_diff)) 75 | 76 | # 找出边缘差异最小的图片 77 | edge_diffs.sort(key=lambda x: x[1]) 78 | return edge_diffs[0][0].split(".")[0] 79 | 80 | 81 | def process_images_in_folder(folder_path): 82 | image_files = [f for f in os.listdir(folder_path) if f.endswith(('.png', '.jpg', '.jpeg'))] 83 | edge_diffs = [] 84 | for image_file in image_files: 85 | image_path = os.path.join(folder_path, image_file) 86 | image = Image.open(image_path) 87 | 88 | image_np = np.array(image) 89 | tiles = split_image(image_np, 4, 4, 'out', image_file.split('.')[0]) 90 | edge_diff = calculate_edge_difference(tiles, 4, 4) 91 | edge_diffs.append((image_file, edge_diff)) 92 | 93 | # 找出边缘差异最小的图片 94 | edge_diffs.sort(key=lambda x: x[1]) 95 | return edge_diffs[0][0].split(".")[0] 96 | 97 | 98 | def run(): 99 | folder_path = './temp' 100 | correct_image = process_images_in_folder(folder_path) 101 | return correct_image 102 | 103 | # ocr = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False, import_onnx_path="models/pikpak4.0.onnx", 104 | # charsets_path="models/charsets.json") 105 | # 106 | # ima_path = 'temp/' 107 | # for file in os.listdir(ima_path): 108 | # with open(f"{ima_path}/{file}", 'rb') as f: 109 | # image_bytes = f.read() 110 | # if ocr.classification(image_bytes) == 'correct': 111 | # # print(f'{file.split(".")[0]}') 112 | # return file.split(".")[0] 113 | # return 1 114 | 115 | 116 | if __name__ == '__main__': 117 | run() 118 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hashlib 3 | import os 4 | import re 5 | import time 6 | import asyncio 7 | import aiohttp 8 | import uuid 9 | import image 10 | import recognize 11 | from rich import print_json 12 | 13 | 14 | 15 | 16 | DEBUG_MODE = False # Debug模式,是否打印请求返回信息 17 | # PROXY = input('请输入代理,如不需要直接回车:') # 代理,如果多次出现IP问题可尝试将自己所用的魔法设置为代理。例如:使用clash则设置为 'http://127.0.0.1:7890' 18 | PROXY = '' 19 | PUSHPLUS_TOKEN = os.getenv('PUSHPLUS_TOKEN') or '' 20 | INVITE_CODE = os.getenv('INVITE_CODE') or input('请输入邀请码: ') 21 | PUSH_MSG = '' 22 | 23 | 24 | # 检查变量 25 | def check_env(): 26 | invite_code_list = [] 27 | if not PUSHPLUS_TOKEN: 28 | print('请按照文档设置PUSHPLUS_TOKEN环境变量') 29 | if not INVITE_CODE: 30 | print('请按照文档设置INVITE_CODE环境变量') 31 | raise Exception('请按照文档设置INVITE_CODE环境变量') 32 | else: 33 | if '@' in INVITE_CODE: 34 | invite_code_list = INVITE_CODE.split('@') 35 | elif '\n' in INVITE_CODE: 36 | invite_code_list = INVITE_CODE.split('\n') 37 | else: 38 | invite_code_list.append(INVITE_CODE) 39 | return invite_code_list 40 | 41 | 42 | # 推送 43 | async def push(content): 44 | if PUSHPLUS_TOKEN: 45 | url = 'http://www.pushplus.plus/send' 46 | data = { 47 | "token": PUSHPLUS_TOKEN, 48 | "title": 'PikPak邀请通知', 49 | "content": content, 50 | } 51 | headers = {'Content-Type': 'application/json'} 52 | try: 53 | async with aiohttp.ClientSession() as session: 54 | async with session.post(url, headers=headers, data=json.dumps(data)) as response: 55 | response_data = await response.json() 56 | if response_data['code'] == 200: 57 | print('推送成功') 58 | else: 59 | print(f'推送失败,{response_data["msg"]}') 60 | except Exception as e: 61 | print(e) 62 | 63 | 64 | # 滑块数据加密 65 | def r(e, t): 66 | n = t - 1 67 | if n < 0: 68 | n = 0 69 | r = e[n] 70 | u = r["row"] // 2 + 1 71 | c = r["column"] // 2 + 1 72 | f = r["matrix"][u][c] 73 | l = t + 1 74 | if l >= len(e): 75 | l = t 76 | d = e[l] 77 | p = l % d["row"] 78 | h = l % d["column"] 79 | g = d["matrix"][p][h] 80 | y = e[t] 81 | m = 3 % y["row"] 82 | v = 7 % y["column"] 83 | w = y["matrix"][m][v] 84 | b = i(f) + o(w) 85 | x = i(w) - o(f) 86 | return [s(a(i(f), o(f))), s(a(i(g), o(g))), s(a(i(w), o(w))), s(a(b, x))] 87 | 88 | 89 | def i(e): 90 | return int(e.split(",")[0]) 91 | 92 | 93 | def o(e): 94 | return int(e.split(",")[1]) 95 | 96 | 97 | def a(e, t): 98 | return str(e) + "^⁣^" + str(t) 99 | 100 | 101 | def s(e): 102 | t = 0 103 | n = len(e) 104 | for r in range(n): 105 | t = u(31 * t + ord(e[r])) 106 | return t 107 | 108 | 109 | def u(e): 110 | t = -2147483648 111 | n = 2147483647 112 | if e > n: 113 | return t + (e - n) % (n - t + 1) - 1 114 | if e < t: 115 | return n - (t - e) % (n - t + 1) + 1 116 | return e 117 | 118 | 119 | def c(e, t): 120 | return s(e + "⁣" + str(t)) 121 | 122 | 123 | def img_jj(e, t, n): 124 | return {"ca": r(e, t), "f": c(n, t)} 125 | 126 | 127 | def md5(input_string): 128 | return hashlib.md5(input_string.encode()).hexdigest() 129 | 130 | 131 | async def get_sign(xid, t): 132 | e = [ 133 | {"alg": "md5", "salt": "KHBJ07an7ROXDoK7Db"}, 134 | {"alg": "md5", "salt": "G6n399rSWkl7WcQmw5rpQInurc1DkLmLJqE"}, 135 | {"alg": "md5", "salt": "JZD1A3M4x+jBFN62hkr7VDhkkZxb9g3rWqRZqFAAb"}, 136 | {"alg": "md5", "salt": "fQnw/AmSlbbI91Ik15gpddGgyU7U"}, 137 | {"alg": "md5", "salt": "/Dv9JdPYSj3sHiWjouR95NTQff"}, 138 | {"alg": "md5", "salt": "yGx2zuTjbWENZqecNI+edrQgqmZKP"}, 139 | {"alg": "md5", "salt": "ljrbSzdHLwbqcRn"}, 140 | {"alg": "md5", "salt": "lSHAsqCkGDGxQqqwrVu"}, 141 | {"alg": "md5", "salt": "TsWXI81fD1"}, 142 | {"alg": "md5", "salt": "vk7hBjawK/rOSrSWajtbMk95nfgf3"} 143 | ] 144 | md5_hash = f"YvtoWO6GNHiuCl7xundefinedmypikpak.com{xid}{t}" 145 | for item in e: 146 | md5_hash += item["salt"] 147 | md5_hash = md5(md5_hash) 148 | return md5_hash 149 | 150 | 151 | async def get_mail(): 152 | json_data = { 153 | "min_name_length": 10, 154 | "max_name_length": 10 155 | } 156 | url = 'https://api.internal.temp-mail.io/api/v3/email/new' 157 | async with aiohttp.ClientSession() as session: 158 | async with session.post(url, json=json_data, ssl=False) as response: 159 | response_data = await response.json() 160 | mail = response_data['email'] 161 | return mail 162 | 163 | 164 | # 获取邮箱的验证码内容 165 | async def get_code(mail, max_retries=10, delay=1): 166 | retries = 0 167 | while retries < max_retries: 168 | url = f'https://api.internal.temp-mail.io/api/v3/email/{mail}/messages' 169 | async with aiohttp.ClientSession() as session: 170 | async with session.get(url, ssl=False) as response: 171 | html = await response.json() 172 | if html: 173 | text = (html[0])['body_text'] 174 | code = re.search('\\d{6}', text).group() 175 | print(f'获取邮箱验证码:{code}') 176 | return code 177 | else: 178 | time.sleep(delay) 179 | retries += 1 180 | print("获取邮箱邮件内容失败,未收到邮件...") 181 | return None 182 | 183 | 184 | async def init(xid, mail): 185 | url = 'https://user.mypikpak.com/v1/shield/captcha/init' 186 | body = { 187 | "client_id": "YvtoWO6GNHiuCl7x", 188 | "action": "POST:/v1/auth/verification", 189 | "device_id": xid, 190 | "captcha_token": "", 191 | "meta": { 192 | "email": mail 193 | } 194 | } 195 | headers = { 196 | 'host': 'user.mypikpak.com', 197 | 'content-length': str(len(json.dumps(body))), 198 | 'accept': '*/*', 199 | 'accept-encoding': 'gzip, deflate, br', 200 | 'referer': 'https://pc.mypikpak.com', 201 | 'sec-fetch-dest': 'empty', 202 | 'sec-fetch-mode': 'cors', 203 | 'sec-fetch-site': 'cross-site', 204 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 205 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 206 | 'accept-language': 'en', 207 | 'content-type': 'application/json', 208 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 209 | 'sec-ch-ua-mobile': '?0', 210 | 'sec-ch-ua-platform': '"Windows"', 211 | 'x-client-id': 'YvtoWO6HNHiuG98x', 212 | 'x-client-version': '2.3.2.4101', 213 | 'x-device-id': xid, 214 | 'x-device-model': 'electron%2F18.3.15', 215 | 'x-device-name': 'PC-Electron', 216 | 'x-device-sign': 'wdi10.ce8280a2dc704cd49f0be1c4eca40059xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 217 | 'x-net-work-type': 'NONE', 218 | 'x-os-version': 'Win64', 219 | 'x-platform-version': '1', 220 | 'x-protocol-version': '301', 221 | 'x-provider-name': 'NONE', 222 | 'x-sdk-version': '6.0.0' 223 | } 224 | async with aiohttp.ClientSession() as session: 225 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 226 | response_data = await response.json() 227 | if 'url' in response_data: 228 | if DEBUG_MODE: 229 | print('初始安全验证:') 230 | print_json(json.dumps(response_data, indent=4)) 231 | return response_data 232 | else: 233 | global PUSH_MSG 234 | PUSH_MSG += f'IP频繁,请一段时间后再试!!!\n' 235 | raise print('IP频繁,请更换IP或者稍后再试!!!\n', response_data['error_description']) 236 | 237 | 238 | async def get_image(xid): 239 | url = "https://user.mypikpak.com/pzzl/gen" 240 | params = { 241 | "deviceid": xid, 242 | "traceid": "" 243 | } 244 | async with aiohttp.ClientSession() as session: 245 | async with session.get(url, params=params, ssl=False, proxy=PROXY) as response: 246 | imgs_json = await response.json() 247 | frames = imgs_json["frames"] 248 | pid = imgs_json['pid'] 249 | traceid = imgs_json['traceid'] 250 | if DEBUG_MODE: 251 | print('滑块ID:') 252 | print_json(json.dumps(pid, indent=4)) 253 | params = { 254 | 'deviceid': xid, 255 | 'pid': pid, 256 | 'traceid': traceid 257 | } 258 | async with session.get(f"https://user.mypikpak.com/pzzl/image", params=params, ssl=False, 259 | proxy=PROXY) as response1: 260 | img_data = await response1.read() 261 | # 保存初始图片 262 | save_image(img_data, f'temp/1.png') 263 | # 保存拼图图片 264 | image.run(f'temp/1.png', frames) 265 | # 识别图片 266 | select_id = recognize.run() 267 | # 删除缓存图片 268 | image.delete_img() 269 | json_data = img_jj(frames, int(select_id), pid) 270 | f = json_data['f'] 271 | npac = json_data['ca'] 272 | params = { 273 | 'pid': pid, 274 | 'deviceid': xid, 275 | 'traceid': traceid, 276 | 'f': f, 277 | 'n': npac[0], 278 | 'p': npac[1], 279 | 'a': npac[2], 280 | 'c': npac[3] 281 | } 282 | async with session.get(f"https://user.mypikpak.com/pzzl/verify", params=params, ssl=False, 283 | proxy=PROXY) as response1: 284 | response_data = await response1.json() 285 | result = {'pid': pid, 'traceid': traceid, 'response_data': response_data} 286 | return result 287 | 288 | 289 | def save_image(img_data, img_path): 290 | if not os.path.exists(os.path.dirname(img_path)): 291 | os.makedirs(os.path.dirname(img_path)) 292 | with open(img_path, "wb") as f: 293 | f.write(img_data) 294 | 295 | 296 | async def get_new_token(result, xid, captcha): 297 | traceid = result['traceid'] 298 | pid = result['pid'] 299 | async with aiohttp.ClientSession() as session: 300 | async with session.get( 301 | f"https://user.mypikpak.com/credit/v1/report?deviceid={xid}&captcha_token={captcha}&type" 302 | f"=pzzlSlider&result=0&data={pid}&traceid={traceid}", ssl=False, proxy=PROXY) as response2: 303 | response_data = await response2.json() 304 | if DEBUG_MODE: 305 | print('获取验证TOKEN:') 306 | print_json(json.dumps(response_data, indent=4)) 307 | return response_data 308 | 309 | 310 | async def verification(captcha_token, xid, mail): 311 | url = 'https://user.mypikpak.com/v1/auth/verification' 312 | body = { 313 | "email": mail, 314 | "target": "ANY", 315 | "usage": "REGISTER", 316 | "locale": "zh-CN", 317 | "client_id": "YvtoWO6GNHiuCl7x" 318 | } 319 | headers = { 320 | 'host': 'user.mypikpak.com', 321 | 'content-length': str(len(json.dumps(body))), 322 | 'accept': '*/*', 323 | 'accept-encoding': 'gzip, deflate, br', 324 | 'referer': 'https://pc.mypikpak.com', 325 | 'sec-fetch-dest': 'empty', 326 | 'sec-fetch-mode': 'cors', 327 | 'sec-fetch-site': 'cross-site', 328 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 329 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 330 | 'accept-language': 'zh-CN', 331 | 'content-type': 'application/json', 332 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 333 | 'sec-ch-ua-mobile': '?0', 334 | 'sec-ch-ua-platform': '"Windows"', 335 | 'x-captcha-token': captcha_token, 336 | 'x-client-id': 'YvtoWO6AYBiuCl7x', 337 | 'x-client-version': '2.3.2.4101', 338 | 'x-device-id': xid, 339 | 'x-device-model': 'electron%2F18.3.15', 340 | 'x-device-name': 'PC-Electron', 341 | 'x-device-sign': 'wdi10.ce6450a2dc704bd49f0be5c4eca70053xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 342 | 'x-net-work-type': 'NONE', 343 | 'x-os-version': 'Win32', 344 | 'x-platform-version': '1', 345 | 'x-protocol-version': '301', 346 | 'x-provider-name': 'NONE', 347 | 'x-sdk-version': '6.0.0' 348 | } 349 | async with aiohttp.ClientSession() as session: 350 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 351 | response_data = await response.json() 352 | if DEBUG_MODE: 353 | print('发送验证码:') 354 | print_json(json.dumps(response_data, indent=4)) 355 | return response_data 356 | 357 | 358 | async def verify(xid, verification_id, code): 359 | url = 'https://user.mypikpak.com/v1/auth/verification/verify' 360 | body = { 361 | "verification_id": verification_id, 362 | "verification_code": code, 363 | "client_id": "YvtoWO6GNHiuCl7x" 364 | } 365 | headers = { 366 | 'host': 'user.mypikpak.com', 367 | 'content-length': str(len(json.dumps(body))), 368 | 'accept': '*/*', 369 | 'accept-encoding': 'gzip, deflate, br', 370 | 'referer': 'https://pc.mypikpak.com', 371 | 'sec-fetch-dest': 'empty', 372 | 'sec-fetch-mode': 'cors', 373 | 'sec-fetch-site': 'cross-site', 374 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 375 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 376 | 'accept-language': 'zh-CN', 377 | 'content-type': 'application/json', 378 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 379 | 'sec-ch-ua-mobile': '?0', 380 | 'sec-ch-ua-platform': '"Windows"', 381 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 382 | 'x-client-version': '2.3.2.4101', 383 | 'x-device-id': xid, 384 | 'x-device-model': 'electron%2F18.3.15', 385 | 'x-device-name': 'PC-Electron', 386 | 'x-device-sign': 'wdi10.ce6450a3dc704cd49f0fe1c4eca40053xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 387 | 'x-net-work-type': 'NONE', 388 | 'x-os-version': 'Win32', 389 | 'x-platform-version': '1', 390 | 'x-protocol-version': '301', 391 | 'x-provider-name': 'NONE', 392 | 'x-sdk-version': '6.0.0' 393 | } 394 | async with aiohttp.ClientSession() as session: 395 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 396 | response_data = await response.json() 397 | if DEBUG_MODE: 398 | print('验证码验证结果:') 399 | print_json(json.dumps(response_data, indent=4)) 400 | return response_data 401 | 402 | 403 | async def signup(xid, mail, code, verification_token): 404 | url = 'https://user.mypikpak.com/v1/auth/signup' 405 | body = { 406 | "email": mail, 407 | "verification_code": code, 408 | "verification_token": verification_token, 409 | "password": "linyuan666", 410 | "client_id": "YvtoWO6GNHiuCl7x" 411 | } 412 | headers = { 413 | 'host': 'user.mypikpak.com', 414 | 'content-length': str(len(json.dumps(body))), 415 | 'accept': '*/*', 416 | 'accept-encoding': 'gzip, deflate, br', 417 | 'referer': 'https://pc.mypikpak.com', 418 | 'sec-fetch-dest': 'empty', 419 | 'sec-fetch-mode': 'cors', 420 | 'sec-fetch-site': 'cross-site', 421 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 422 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 423 | 'accept-language': 'zh-CN', 424 | 'content-type': 'application/json', 425 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 426 | 'sec-ch-ua-mobile': '?0', 427 | 'sec-ch-ua-platform': '"Windows"', 428 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 429 | 'x-client-version': '2.3.2.4101', 430 | 'x-device-id': xid, 431 | 'x-device-model': 'electron%2F18.3.15', 432 | 'x-device-name': 'PC-Electron', 433 | 'x-device-sign': 'wdi10.ce6450a2dc704cd49f0be1c4eca40053xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 434 | 'x-net-work-type': 'NONE', 435 | 'x-os-version': 'Win32', 436 | 'x-platform-version': '1', 437 | 'x-protocol-version': '301', 438 | 'x-provider-name': 'NONE', 439 | 'x-sdk-version': '6.0.0' 440 | } 441 | async with aiohttp.ClientSession() as session: 442 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 443 | response_data = await response.json() 444 | if DEBUG_MODE: 445 | print('注册结果:') 446 | print_json(json.dumps(response_data, indent=4)) 447 | return response_data 448 | 449 | 450 | async def init1(xid, access_token, sub, sign, t): 451 | url = 'https://user.mypikpak.com/v1/shield/captcha/init' 452 | body = { 453 | "client_id": "YvtoWO6GNHiuCl7x", 454 | "action": "POST:/vip/v1/activity/invite", 455 | "device_id": xid, 456 | "captcha_token": access_token, 457 | "meta": { 458 | "captcha_sign": "1." + sign, 459 | "client_version": "undefined", 460 | "package_name": "mypikpak.com", 461 | "user_id": sub, 462 | "timestamp": t 463 | }, 464 | } 465 | headers = { 466 | 'host': 'user.mypikpak.com', 467 | 'content-length': str(len(json.dumps(body))), 468 | 'accept': '*/*', 469 | 'accept-encoding': 'gzip, deflate, br', 470 | 'accept-language': 'zh-CN', 471 | 'referer': 'https://pc.mypikpak.com', 472 | 'sec-fetch-dest': 'empty', 473 | 'sec-fetch-mode': 'cors', 474 | 'sec-fetch-site': 'cross-site', 475 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 476 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 477 | 'content-type': 'application/json', 478 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 479 | 'sec-ch-ua-mobile': '?0', 480 | 'sec-ch-ua-platform': '"Windows"', 481 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 482 | 'x-client-version': '2.3.2.4101', 483 | 'x-device-id': xid, 484 | 'x-device-model': 'electron%2F18.3.15', 485 | 'x-device-name': 'PC-Electron', 486 | 'x-device-sign': 'wdi10.ce6450a2dc704cd49f0be1c4eca40053xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 487 | 'x-net-work-type': 'NONE', 488 | 'x-os-version': 'Win32', 489 | 'x-platform-version': '1', 490 | 'x-protocol-version': '301', 491 | 'x-provider-name': 'NONE', 492 | 'x-sdk-version': '6.0.0' 493 | } 494 | async with aiohttp.ClientSession() as session: 495 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 496 | response_data = await response.json() 497 | if DEBUG_MODE: 498 | print('二次安全验证:') 499 | print_json(json.dumps(response_data, indent=4)) 500 | return response_data 501 | 502 | 503 | async def invite(access_token, captcha_token, xid): 504 | url = 'https://api-drive.mypikpak.com/vip/v1/activity/invite' 505 | body = { 506 | "apk_extra": { 507 | "invite_code": "" 508 | } 509 | } 510 | headers = { 511 | 'host': 'api-drive.mypikpak.com', 512 | 'content-length': str(len(json.dumps(body))), 513 | 'accept': '*/*', 514 | 'accept-encoding': 'gzip, deflate, br', 515 | 'accept-language': 'zh-CN', 516 | 'authorization': 'Bearer ' + access_token, 517 | 'referer': 'https://pc.mypikpak.com', 518 | 'sec-fetch-dest': 'empty', 519 | 'sec-fetch-mode': 'cors', 520 | 'sec-fetch-site': 'cross-site', 521 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) PikPak/2.3.2.4101 ' 522 | 'Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 523 | 'content-type': 'application/json', 524 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 525 | 'sec-ch-ua-mobile': '?0', 526 | 'sec-ch-ua-platform': '"Windows"', 527 | 'x-captcha-token': captcha_token, 528 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 529 | 'x-client-version': '2.3.2.4101', 530 | 'x-device-id': xid, 531 | 'x-system-language': 'zh-CN' 532 | } 533 | async with aiohttp.ClientSession() as session: 534 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 535 | response_data = await response.json() 536 | if DEBUG_MODE: 537 | print('获取邀请:') 538 | print_json(json.dumps(response_data, indent=4)) 539 | return response_data 540 | 541 | 542 | async def init2(xid, access_token, sub, sign, t): 543 | url = 'https://user.mypikpak.com/v1/shield/captcha/init' 544 | body = { 545 | "client_id": "YvtoWO6GNHiuCl7x", 546 | "action": "POST:/vip/v1/order/activation-code", 547 | "device_id": xid, 548 | "captcha_token": access_token, 549 | "meta": { 550 | "captcha_sign": "1." + sign, 551 | "client_version": "undefined", 552 | "package_name": "mypikpak.com", 553 | "user_id": sub, 554 | "timestamp": t 555 | }, 556 | } 557 | headers = { 558 | 'host': 'user.mypikpak.com', 559 | 'content-length': str(len(json.dumps(body))), 560 | 'accept': '*/*', 561 | 'accept-encoding': 'gzip, deflate, br', 562 | 'accept-language': 'zh-CN', 563 | 'referer': 'https://pc.mypikpak.com', 564 | 'sec-fetch-dest': 'empty', 565 | 'sec-fetch-mode': 'cors', 566 | 'sec-fetch-site': 'cross-site', 567 | 'user-agent': 'MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 568 | 'PikPak/2.3.2.4101 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 569 | 'content-type': 'application/json', 570 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 571 | 'sec-ch-ua-mobile': '?0', 572 | 'sec-ch-ua-platform': '"Windows"', 573 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 574 | 'x-client-version': '2.3.2.4101', 575 | 'x-device-id': xid, 576 | 'x-device-model': 'electron%2F18.3.15', 577 | 'x-device-name': 'PC-Electron', 578 | 'x-device-sign': 'wdi10.ce6450a2dc704cd49f0be1c4eca40053xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 579 | 'x-net-work-type': 'NONE', 580 | 'x-os-version': 'Win32', 581 | 'x-platform-version': '1', 582 | 'x-protocol-version': '301', 583 | 'x-provider-name': 'NONE', 584 | 'x-sdk-version': '6.0.0' 585 | } 586 | async with aiohttp.ClientSession() as session: 587 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 588 | response_data = await response.json() 589 | if DEBUG_MODE: 590 | print('三次安全验证:') 591 | print_json(json.dumps(response_data, indent=4)) 592 | return response_data 593 | 594 | 595 | async def activation_code(access_token, captcha, xid, in_code): 596 | url = 'https://api-drive.mypikpak.com/vip/v1/order/activation-code' 597 | body = { 598 | "activation_code": in_code, 599 | "page": "invite" 600 | } 601 | headers = { 602 | 'host': 'api-drive.mypikpak.com', 603 | 'content-length': str(len(json.dumps(body))), 604 | 'accept': '*/*', 605 | 'accept-encoding': 'gzip, deflate, br', 606 | 'accept-language': 'zh-CN', 607 | 'authorization': 'Bearer ' + access_token, 608 | 'referer': 'https://pc.mypikpak.com', 609 | 'sec-fetch-dest': 'empty', 610 | 'sec-fetch-mode': 'cors', 611 | 'sec-fetch-site': 'cross-site', 612 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) PikPak/2.3.2.4101 ' 613 | 'Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36', 614 | 'content-type': 'application/json', 615 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100"', 616 | 'sec-ch-ua-mobile': '?0', 617 | 'sec-ch-ua-platform': '"Windows"', 618 | 'x-captcha-token': captcha, 619 | 'x-client-id': 'YvtoWO6GNHiuCl7x', 620 | 'x-client-version': '2.3.2.4101', 621 | 'x-device-id': xid, 622 | 'x-system-language': 'zh-CN' 623 | } 624 | 625 | async with aiohttp.ClientSession() as session: 626 | try: 627 | async with aiohttp.ClientSession() as session: 628 | async with session.post(url, json=body, headers=headers, ssl=False, proxy=PROXY) as response: 629 | response_data = await response.json() 630 | if DEBUG_MODE: 631 | print('填写邀请:') 632 | print_json(json.dumps(response_data, indent=4)) 633 | return response_data 634 | except Exception as error: 635 | print('Error:', error) 636 | raise error 637 | 638 | 639 | async def main(incode): 640 | global PUSH_MSG 641 | try: 642 | start_time = time.time() 643 | xid = str(uuid.uuid4()).replace("-", "") 644 | mail = await get_mail() 645 | Init = await init(xid, mail) 646 | while True: 647 | print('验证滑块中...') 648 | img_info = await get_image(xid) 649 | if img_info['response_data']['result'] == 'accept': 650 | print('验证通过!!!') 651 | break 652 | else: 653 | print('验证失败, 重新验证滑块中...') 654 | captcha_token_info = await get_new_token(img_info, xid, Init['captcha_token']) 655 | verification_id = await verification(captcha_token_info['captcha_token'], xid, mail) 656 | code = await get_code(mail) 657 | verification_response = await verify(xid, verification_id['verification_id'], code) 658 | signup_response = await signup(xid, mail, code, verification_response['verification_token']) 659 | current_time = str(int(time.time())) 660 | sign = await get_sign(xid, current_time) 661 | init1_response = await init1(xid, signup_response['access_token'], signup_response['sub'], sign, current_time) 662 | await invite(signup_response['access_token'], init1_response['captcha_token'], xid) 663 | init2_response = await init2(xid, signup_response['access_token'], signup_response['sub'], sign, current_time) 664 | activation = await activation_code(signup_response['access_token'], init2_response['captcha_token'], xid, 665 | incode) 666 | end_time = time.time() 667 | run_time = f"{(end_time - start_time):.2f}" 668 | if activation['add_days'] == 5: 669 | print(f'邀请码: {incode} ==> 邀请成功, 用时: {run_time} 秒') 670 | print(f'邮箱: {mail}') 671 | print(f'密码: linyuan666') 672 | PUSH_MSG += f'邀请码: {incode} ==> 邀请成功\n邮箱: {mail}\n密码: linyuan666\n' 673 | return 674 | else: 675 | print(f'邀请码: {incode} ==> 邀请失败, 用时: {run_time} 秒') 676 | PUSH_MSG += f'邀请码: {incode} ==> 邀请失败\n' 677 | # input('按回车键再次邀请!!!') 678 | await main(incode) 679 | except Exception as e: 680 | if '环境变量' in str(e): 681 | return 682 | print(f'异常捕获:{e}') 683 | PUSH_MSG += f'邀请异常:{e}\n' 684 | # print('请检查网络环境,(开启科学上网)重试!!!') 685 | # input('按回车键重试!!!') 686 | # await main() 687 | 688 | 689 | async def run(): 690 | global PUSH_MSG 691 | invite_code_list = check_env() 692 | for i in range(len(invite_code_list)): 693 | print(f'=========正在邀请第 {i + 1} 个邀请码=========') 694 | PUSH_MSG += f'=========第 {i + 1} 个邀请码=========\n' 695 | invite_code = invite_code_list[i] 696 | await main(invite_code) 697 | await push(PUSH_MSG) 698 | 699 | 700 | asyncio.run(run()) 701 | --------------------------------------------------------------------------------