389 lines
17 KiB
Python
389 lines
17 KiB
Python
# Source: https://github.com/lksky8/sign-ql/blob/main/PPark.py
|
||
# Raw: https://raw.githubusercontent.com/lksky8/sign-ql/main/PPark.py
|
||
# Repo: lksky8/sign-ql
|
||
# Path: PPark.py
|
||
# UploadedAt: 2025-09-22T11:01:17Z
|
||
# SHA256: 120e73308bc1ccfb9db8ebd87357364850257a25ba1bc039d67c2967fe6c8e19
|
||
# Category: web版/账密
|
||
# Evidence: web/H5关键词 + username/password/login
|
||
|
||
"""
|
||
PP停车签到
|
||
|
||
作者:https://github.com/lksky8/sign-ql
|
||
最后更新日期:2025-9-22
|
||
食用方法:变量输入export pp_token=账号1#token1&账号2#token2
|
||
支持多用户运行
|
||
多用户用&或者@隔开
|
||
例如账号1:13800000000#eyJhbGciOiJIUzI1NiJ9.eyj 账号2: 139000000000#eyJhbGciOiJIUzI1NiJ9.eyj
|
||
则变量为
|
||
export pp_token="13800000000#eyJhbGciOiJIUzI1NiJ9.eyj&13900000000#eyJhbGciOiJIUzI1NiJ9.eyj"
|
||
每天运行两次否则token会过期
|
||
|
||
cron: 0 40 0,12 * * *
|
||
"""
|
||
import requests
|
||
import base64
|
||
from urllib.parse import quote
|
||
import json
|
||
import hashlib
|
||
import time
|
||
import os
|
||
import re
|
||
|
||
KEY_ENCRYPT = "2363ECDFC54A5AF12477D3D45333A19F" # key1 用于加密
|
||
KEY_DECRYPT = "466d67cf8f9810707404fae5ed172b8e" # key2 用于解密
|
||
WX_ENCRYPT = "riegh^ee:w0fok5je5eeS{eecaes1nep"
|
||
|
||
send_msg = ''
|
||
one_msg = ''
|
||
|
||
def Log(cont=''):
|
||
global send_msg, one_msg
|
||
if cont:
|
||
one_msg += f'\n{cont}'
|
||
send_msg += f'\n{cont}'
|
||
|
||
|
||
|
||
def send_notification_message(title):
|
||
try:
|
||
from notify import send
|
||
print("加载通知服务成功!")
|
||
send(title, send_msg)
|
||
except Exception as e:
|
||
if e:
|
||
print('发送通知消息失败!')
|
||
|
||
api_headers = {
|
||
'Host': 'api.660pp.com',
|
||
'accept': '*/*',
|
||
'content-type': 'application/x-www-form-urlencoded',
|
||
'rest_api_type': '1',
|
||
'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Parking/4.3.2 (iOS 16.6.1; iPhone15,2; Build/1312) NetType/WIFI',
|
||
'accept-language': 'zh_CN',
|
||
}
|
||
|
||
|
||
|
||
wx_headers = {
|
||
'Host': 'user-api.4pyun.com',
|
||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541022) XWEB/16467',
|
||
'xweb_xhr': '1',
|
||
'content-type': 'application/x-www-form-urlencoded',
|
||
'accept': '*/*',
|
||
'sec-fetch-site': 'cross-site',
|
||
'sec-fetch-mode': 'cors',
|
||
'sec-fetch-dest': 'empty',
|
||
'referer': 'https://servicewechat.com/wxa204074068ad40ef/879/page-frame.html',
|
||
'accept-language': 'zh-CN,zh;q=0.9',
|
||
'priority': 'u=1, i',
|
||
}
|
||
|
||
def transform(bytes_data: bytearray, key: str) -> bytearray:
|
||
len_key = len(key)
|
||
len_b = len(bytes_data)
|
||
for i in range(len_b):
|
||
index = (len_b - i) % len_key
|
||
key_byte = ord(key[index])
|
||
in_byte = bytes_data[i]
|
||
flipped = in_byte ^ 0xFF
|
||
bytes_data[i] = key_byte ^ flipped
|
||
return bytes_data
|
||
|
||
def encrypt(arg: str, key: str) -> str:
|
||
"""加密,对应提供的 encrypt 方法"""
|
||
bytes_data = bytearray(arg.encode('utf-8'))
|
||
transformed = transform(bytes_data, key)
|
||
return base64.b64encode(transformed).decode('utf-8')
|
||
|
||
def decrypt(arg: str) -> str:
|
||
"""解密,对应提供的 decrypt 方法"""
|
||
bytes_data = bytearray(base64.b64decode(arg))
|
||
transformed = transform(bytes_data, KEY_DECRYPT)
|
||
return transformed.decode('utf-8')
|
||
|
||
def save_or_update_phone_data(phone_key, new_data, file_path='PPark_data.json'):
|
||
if os.path.exists(file_path):
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
existing_data = json.load(f)
|
||
else:
|
||
existing_data = {}
|
||
|
||
if phone_key in existing_data:
|
||
existing_data[phone_key].update(new_data) # 合并新旧数据
|
||
print(f"[{phone_key}] 数据已更新!")
|
||
else:
|
||
existing_data[phone_key] = new_data # 新增数据
|
||
print(f"[{phone_key}] 新增数据!")
|
||
|
||
with open(file_path, 'w', encoding='utf-8') as f:
|
||
json.dump(existing_data, f, ensure_ascii=False, indent=4)
|
||
|
||
def get_user_ids_and_tokens(file_path='PPark_data.json'):
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
phone_data = json.load(f)
|
||
results = []
|
||
for phone_key, data in phone_data.items():
|
||
token = data.get('refresh_token')
|
||
if token:
|
||
results.append({
|
||
'phone_key': phone_key,
|
||
'refresh_token': token,
|
||
'user_name': data.get('user_name'),
|
||
'user_id': data.get('user_id')
|
||
})
|
||
return results
|
||
except FileNotFoundError:
|
||
print(f"文件 {file_path} 不存在!")
|
||
return []
|
||
except json.JSONDecodeError:
|
||
print(f"文件 {file_path} 格式错误!")
|
||
return []
|
||
|
||
def get_userdata(u):
|
||
user_phone = u.split('#')[0]
|
||
user_token = u.split('#')[1]
|
||
|
||
try:
|
||
with open('PPark_data.json', 'r', encoding='utf-8') as f:
|
||
cache_data = json.load(f)
|
||
except (FileNotFoundError, json.JSONDecodeError):
|
||
cache_data = {}
|
||
|
||
if user_phone not in cache_data:
|
||
print(f'[{user_phone}] 不存在于缓存中,尝试初始化数据')
|
||
init_refresh_token(user_phone,user_token)
|
||
else:
|
||
print(f'[{user_phone}] 已存在于缓存中,直接读取数据')
|
||
refresh_token(user_phone,user_token,cache_data[user_phone]['refresh_token'])
|
||
old_user_token = cache_data[user_phone]['init_token']
|
||
if old_user_token != user_token:
|
||
print(f'[{user_phone}] token已更新,尝试刷新')
|
||
init_refresh_token(user_phone,user_token)
|
||
|
||
|
||
def md5_encrypt(s: str):
|
||
return hashlib.md5(s.encode('utf-8')).hexdigest()
|
||
|
||
|
||
def get_user_info(phone: str, token: str):
|
||
headers = wx_headers.copy()
|
||
headers['authorization'] = f'Bearer {token}'
|
||
try:
|
||
response = requests.get('https://user-api.4pyun.com/rest/2.0/user/whoami',headers=headers)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
print(f"[{response_json['payload']['nickname']}] 获取用户信息:成功")
|
||
return response_json['payload']['nickname'], response_json['payload']['identity']
|
||
else:
|
||
print(f"[{phone}] 获取用户信息:失败:{response_json}")
|
||
return None
|
||
except requests.RequestException as e:
|
||
print(f"[{phone}] 获取用户信息_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{phone}] 获取用户信息_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{phone}] 获取用户信息_其他异常:{e}")
|
||
|
||
def refresh_token(mobile: str, i_token: str, r_token: str):
|
||
headers = api_headers.copy()
|
||
headers['rest_api_token'] = r_token
|
||
try:
|
||
response = requests.put('https://api.660pp.com/rest/1.6/user/token', headers=headers)
|
||
response_json = response.json()
|
||
# print(response_json)
|
||
if response_json['code'] == '1001':
|
||
result = json.loads(decrypt(response_json['result']))
|
||
print(f"[{mobile}] 刷新token成功")
|
||
save_or_update_phone_data(mobile,{'init_token': i_token,'refresh_token': result['token']})
|
||
elif response_json['code'] == '401':
|
||
print(f"[{mobile}] 刷新token失败:token已失效")
|
||
Log(f"[{mobile}] 刷新token失败:token已失效")
|
||
else:
|
||
print(f"[{mobile}] 刷新token失败:{response_json}")
|
||
except requests.RequestException as e:
|
||
print(f"[{mobile}] 刷新token_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{mobile}] 刷新token_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{mobile}] 刷新token_其他异常:{e}")
|
||
|
||
|
||
def init_refresh_token(mobile: str, token: str):
|
||
headers = api_headers.copy()
|
||
headers['rest_api_token'] = token
|
||
try:
|
||
response = requests.put('https://api.660pp.com/rest/1.6/user/token', headers=headers)
|
||
response_json = response.json()
|
||
# print(response_json)
|
||
if response_json['code'] == '1001':
|
||
result = json.loads(decrypt(response_json['result']))
|
||
user_name, user_id = get_user_info(mobile, token)
|
||
save_or_update_phone_data(mobile,{'user_name': user_name, 'user_id': user_id, 'init_token': token, 'refresh_token': result['token']})
|
||
print(f"[{mobile}] 刷新token成功")
|
||
elif response_json['code'] == '401':
|
||
print(f"[{mobile}] 刷新token失败:token已失效")
|
||
Log(f"[{mobile}] 刷新token失败:token已失效")
|
||
else:
|
||
print(f"[{mobile}] 刷新token失败:{response_json}")
|
||
except requests.RequestException as e:
|
||
print(f"[{mobile}] 刷新token_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{mobile}] 刷新token_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{mobile}] 刷新token_其他异常:{e}")
|
||
|
||
def day_sign(nickname: str, token: str):
|
||
headers = api_headers.copy()
|
||
headers['rest_api_token'] = token
|
||
try:
|
||
response = requests.get('https://api.660pp.com/rest/1.6/reward/bonus?yc7OyqO6qQ%3D%3D=rL7JhL/NrriG2tPZ2aegog%3D%3D', headers=headers)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
result = json.loads(decrypt(response_json['result']))
|
||
message = result['message'].replace('\r\n', '')
|
||
if '签到获得' in result['message'] or '已连续签到'in result['message']:
|
||
print(f"[{nickname}] 签到成功:{message} 已累计获得:{result['combo']} 积分")
|
||
Log(f"[{nickname}] 签到成功:{message} 已累计获得:{result['combo']} 积分")
|
||
elif message == '您已签到成功' :
|
||
print(f"[{nickname}] 签到失败:已签到")
|
||
Log(f"[{nickname}] 已签到")
|
||
else:
|
||
print(f"[{nickname}] 签到失败:{message}")
|
||
Log(f"[{nickname}] 签到失败:{message}")
|
||
else:
|
||
print(f"[{nickname}] 获取签到数据失败:{response_json}")
|
||
except requests.RequestException as e:
|
||
print(f"[{nickname}] 签到_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{nickname}] 签到_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{nickname}] 签到_其他异常:{e}")
|
||
|
||
def get_task(nickname: str, token: str):
|
||
headers = wx_headers.copy()
|
||
headers['authorization'] = f'Bearer {token}'
|
||
try:
|
||
response = requests.get('https://user-api.4pyun.com/rest/2.0/bonus/reward/task/list?%2B9Hnx%2FPy=7bL7p%2FqgoK77uPOi%2B8WjqP%2Fw', headers=headers)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
video_task = [task for task in response_json['payload']['row'] if task['name'] == '看视频得积分'][0]
|
||
referer_url = video_task['referer_url']
|
||
print(f'[{nickname}] 获取视频任务数据成功')
|
||
return referer_url.split('&voucher=')[1]
|
||
else:
|
||
print(f"[{nickname}] 获取任务_失败:{response_json}")
|
||
Log(f"[{nickname}] 获取视频任务数据失败:{response_json}")
|
||
return None
|
||
except requests.RequestException as e:
|
||
print(f"[{nickname}] 获取任务_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{nickname}] 获取任务_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{nickname}] 获取任务_其他异常:{e}")
|
||
|
||
def complete_task(nickname: str, token: str, task_id: str):
|
||
headers = wx_headers.copy()
|
||
headers['authorization'] = f'Bearer {token}'
|
||
headers['content-type'] = 'application/json;charset=utf-8'
|
||
try:
|
||
data = '{"app_id":"wxa204074068ad40ef","purpose":"reward:motivate:advertising","voucher":"voucher_data"}'.replace('voucher_data', task_id)
|
||
encrypt_data = encrypt(data,WX_ENCRYPT)
|
||
response = requests.post('https://user-api.4pyun.com/rest/2.0/bonus/reward/task/complete', headers=headers, data=encrypt_data)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
print(f"[{nickname}] 看视频:任务完成")
|
||
headers['content-type'] = 'application/x-www-form-urlencoded'
|
||
response = requests.get('https://user-api.4pyun.com/rest/2.0/bonus/reward/acquire?%2B9Hnx%2FPy=7bL7p%2FqgoK77uPOi%2B8WjqP%2Fw&6u%2FT5%2Ffp8w%3D%3D=%2Fv%2Fp%2Fej%2BvsH17qPs9L7xqvir%2FqDo7sjk8fTx', headers=headers)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
print(f"[{nickname}] 看视频:任务奖励已领取,{response_json['payload']['message']}")
|
||
Log(f"[{nickname}] 看视频:任务奖励已领取,{response_json['payload']['message']}")
|
||
elif response_json['message'] == '已达积分领取次数上限':
|
||
print(f"[{nickname}] 看视频:积分已领取")
|
||
else:
|
||
print(f"[{nickname}] 看视频:{response_json['message']}")
|
||
Log(f"[{nickname}] 看视频:{response_json['message']}")
|
||
else:
|
||
print(f"[{nickname}] 看视频任务失败:{response_json}")
|
||
Log(f"[{nickname}] 看视频任务失败:{response_json}")
|
||
except requests.RequestException as e:
|
||
print(f"[{nickname}] 看视频任务_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{nickname}] 看视频任务_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{nickname}] 看视频任务_其他异常:{e}")
|
||
|
||
def reward_balance(nickname: str, token: str, user_id: str):
|
||
headers = wx_headers.copy()
|
||
headers['authorization'] = f'Bearer {token}'
|
||
try:
|
||
response = requests.get(f'https://user-api.4pyun.com/rest/2.0/reward/balance?rP7%2Fz%2BPx7u8%3D={quote(encrypt(user_id,WX_ENCRYPT))}&7%2BnE5cfz8g%3D%3D={quote(encrypt(user_id,WX_ENCRYPT))}&%2Fbb%2F6P7j4erz=pw%3D%3D', headers=headers)
|
||
response.raise_for_status()
|
||
response_json = response.json()
|
||
if response_json['code'] == '1001':
|
||
print(f"[{nickname}] 积分余额:{response_json['payload']['balance']}")
|
||
Log(f"[{nickname}] 积分余额:{response_json['payload']['balance']}")
|
||
else:
|
||
print(f"[{nickname}] 获取积分余额_失败:{response_json}")
|
||
Log(f"[{nickname}] 获取积分余额_失败:{response_json}")
|
||
except requests.RequestException as e:
|
||
print(f"[{nickname}] 获取积分余额_请求异常:{e}")
|
||
except json.JSONDecodeError:
|
||
print(f"[{nickname}] 获取积分余额_响应不是有效的JSON格式")
|
||
except Exception as e:
|
||
print(f"[{nickname}] 获取积分余额_其他异常:{e}")
|
||
|
||
if __name__ == '__main__':
|
||
if 'pp_token' in os.environ:
|
||
pp_token = re.split("@|&", os.environ.get("pp_token"))
|
||
print(f'查找到{len(pp_token)}个账号')
|
||
else:
|
||
pp_token = []
|
||
print('请填入pp_token变量')
|
||
Log('请填入pp_token变量')
|
||
|
||
try:
|
||
if pp_token:
|
||
for pp_token_item in pp_token:
|
||
get_userdata(pp_token_item)
|
||
user_data = get_user_ids_and_tokens()
|
||
if user_data:
|
||
print(f"配置缓存中读取到{len(user_data)}个账号")
|
||
print('*' * 50)
|
||
z = 1
|
||
for item in user_data:
|
||
print('-' * 50)
|
||
print(f'登录第{z}个账号>>>>>>')
|
||
phone_key = item.get('phone_key')
|
||
new_token = item.get('refresh_token')
|
||
user_name = item.get('user_name')
|
||
user_id = item.get('user_id')
|
||
if new_token:
|
||
day_sign(user_name,new_token)
|
||
for _ in range(2):
|
||
voucher = get_task(user_name, new_token)
|
||
if voucher:
|
||
complete_task(user_name, new_token, voucher)
|
||
time.sleep(5)
|
||
reward_balance(user_name,new_token,user_id)
|
||
time.sleep(3)
|
||
Log('-'*30)
|
||
z += 1
|
||
else:
|
||
print("未读取到用户数据,程序退出")
|
||
exit(0)
|
||
except Exception as e:
|
||
print(f'程序异常:{e}')
|
||
try:
|
||
send_notification_message(title='PP停车') # 发送通知
|
||
except Exception as e:
|
||
print('推送失败:' + str(e))
|