上传文件至「TongXun/Telecom」
This commit is contained in:
@@ -0,0 +1,255 @@
|
|||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import execjs
|
||||||
|
import base64
|
||||||
|
import random
|
||||||
|
import certifi
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
|
import requests
|
||||||
|
from http import cookiejar
|
||||||
|
from Crypto.Cipher import DES3
|
||||||
|
from Crypto.Util.Padding import pad, unpad
|
||||||
|
from aiohttp import ClientSession, TCPConnector
|
||||||
|
import httpx
|
||||||
|
httpx._config.DEFAULT_CIPHERS += ":ALL:@SECLEVEL=1"
|
||||||
|
diffValue = 2
|
||||||
|
filename='Cache.js'
|
||||||
|
if os.path.exists(filename):
|
||||||
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
fileContent = file.read()
|
||||||
|
else:
|
||||||
|
fileContent=''
|
||||||
|
|
||||||
|
class BlockAll(cookiejar.CookiePolicy):
|
||||||
|
return_ok = set_ok = domain_return_ok = path_return_ok = lambda self, *args, **kwargs: False
|
||||||
|
netscape = True
|
||||||
|
rfc2965 = hide_cookie2 = False
|
||||||
|
|
||||||
|
def printn(m):
|
||||||
|
print(f'\n{m}')
|
||||||
|
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
context.set_ciphers('DEFAULT@SECLEVEL=1') # 低安全级别0/1
|
||||||
|
context.check_hostname = False # 禁用主机
|
||||||
|
context.verify_mode = ssl.CERT_NONE # 禁用证书
|
||||||
|
|
||||||
|
class DESAdapter(requests.adapters.HTTPAdapter):
|
||||||
|
def init_poolmanager(self, *args, **kwargs):
|
||||||
|
kwargs['ssl_context'] = context
|
||||||
|
return super().init_poolmanager(*args, **kwargs)
|
||||||
|
|
||||||
|
requests.DEFAULT_RETRIES = 0
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
ss = requests.session()
|
||||||
|
ss.headers = {"User-Agent": "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36", "Referer": "https://wapact.189.cn:9001/JinDouMall/JinDouMall_independentDetails.html"}
|
||||||
|
ss.mount('https://', DESAdapter())
|
||||||
|
ss.cookies.set_policy(BlockAll())
|
||||||
|
runTime = 0
|
||||||
|
sleepTime = 1
|
||||||
|
key = b'1234567`90koiuyhgtfrdews'
|
||||||
|
iv = 8 * b'\0'
|
||||||
|
|
||||||
|
def encrypt(text):
|
||||||
|
cipher = DES3.new(key, DES3.MODE_CBC, iv)
|
||||||
|
ciphertext = cipher.encrypt(pad(text.encode(), DES3.block_size))
|
||||||
|
return ciphertext.hex()
|
||||||
|
|
||||||
|
def decrypt(text):
|
||||||
|
ciphertext = bytes.fromhex(text)
|
||||||
|
cipher = DES3.new(key, DES3.MODE_CBC, iv)
|
||||||
|
plaintext = unpad(cipher.decrypt(ciphertext), DES3.block_size)
|
||||||
|
return plaintext.decode()
|
||||||
|
|
||||||
|
def initCookie(getUrl='https://wapact.189.cn:9001/gateway/standQuery/detailNew/exchange'):
|
||||||
|
global js_code_ym, fileContent
|
||||||
|
cookie = ''
|
||||||
|
response = httpx.post(getUrl)
|
||||||
|
content = response.text.split(' content="')[2].split('" r=')[0]
|
||||||
|
code1 = response.text.split('$_ts=window')[1].split('</script><script type="text/javascript"')[0]
|
||||||
|
code1Content = '$_ts=window' + code1
|
||||||
|
Url = response.text.split('$_ts.lcd();</script><script type="text/javascript" charset="utf-8" src="')[1].split('" r=')[0]
|
||||||
|
urls = getUrl.split('/')
|
||||||
|
rsurl = urls[0] + '//' + urls[2] + Url
|
||||||
|
filename = 'Cache.js'
|
||||||
|
if fileContent == '':
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
fileRes = httpx.get(rsurl)
|
||||||
|
fileContent = fileRes.text
|
||||||
|
if fileRes.status_code == 200:
|
||||||
|
with open(filename, 'w', encoding='utf-8') as file:
|
||||||
|
file.write(fileRes.text)
|
||||||
|
else:
|
||||||
|
print(f"Failed to download {rsurl}. Status code: {fileRes.status_code}")
|
||||||
|
if response.headers['Set-Cookie']:
|
||||||
|
cookie = response.headers['Set-Cookie'].split(';')[0].split('=')[1]
|
||||||
|
runJs = js_code_ym.replace('content_code', content).replace("'ts_code'", code1Content + fileContent)
|
||||||
|
execjsRun = RefererCookie(runJs)
|
||||||
|
return {
|
||||||
|
'cookie': cookie,
|
||||||
|
'execjsRun': execjsRun
|
||||||
|
}
|
||||||
|
|
||||||
|
def RefererCookie(runJs):
|
||||||
|
try:
|
||||||
|
execjsRun = execjs.compile(runJs)
|
||||||
|
return execjsRun
|
||||||
|
except execjs._exceptions.CompileError as e:
|
||||||
|
print(f"JavaScript 编译错误: {e}")
|
||||||
|
except execjs._exceptions.RuntimeError as e:
|
||||||
|
print(f"JavaScript 运行时错误: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"其他错误: {e}")
|
||||||
|
|
||||||
|
js_code_ym = '''delete __filename
|
||||||
|
delete __dirname
|
||||||
|
ActiveXObject = undefined
|
||||||
|
|
||||||
|
window = global;
|
||||||
|
|
||||||
|
content="content_code"
|
||||||
|
|
||||||
|
navigator = {"platform": "Linux aarch64"}
|
||||||
|
navigator = {"userAgent": "CtClient;11.0.0;Android;13;22081212C;NTIyMTcw!#!MTUzNzY"}
|
||||||
|
|
||||||
|
location={
|
||||||
|
"href": "https://",
|
||||||
|
"origin": "",
|
||||||
|
"protocol": "",
|
||||||
|
"host": "",
|
||||||
|
"hostname": "",
|
||||||
|
"port": "",
|
||||||
|
"pathname": "",
|
||||||
|
"search": "",
|
||||||
|
"hash": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i = {length: 0}
|
||||||
|
base = {length: 0}
|
||||||
|
div = {
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('div中的getElementsByTagName:', res)
|
||||||
|
if (res === 'i') {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return '<div></div>'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script = {
|
||||||
|
|
||||||
|
}
|
||||||
|
meta = [
|
||||||
|
{charset:"UTF-8"},
|
||||||
|
{
|
||||||
|
content: content,
|
||||||
|
getAttribute: function (res) {
|
||||||
|
console.log('meta中的getAttribute:', res)
|
||||||
|
if (res === 'r') {
|
||||||
|
return 'm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentNode: {
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('meta中的removeChild:', res)
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
form = '<form></form>'
|
||||||
|
|
||||||
|
window.addEventListener= function (res) {
|
||||||
|
console.log('window中的addEventListener:', res)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
document = {
|
||||||
|
createElement: function (res) {
|
||||||
|
console.log('document中的createElement:', res)
|
||||||
|
|
||||||
|
if (res === 'div') {
|
||||||
|
return div
|
||||||
|
} else if (res === 'form') {
|
||||||
|
return form
|
||||||
|
}
|
||||||
|
else{return res}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
addEventListener: function (res) {
|
||||||
|
console.log('document中的addEventListener:', res)
|
||||||
|
|
||||||
|
},
|
||||||
|
appendChild: function (res) {
|
||||||
|
console.log('document中的appendChild:', res)
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('document中的removeChild:', res)
|
||||||
|
},
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('document中的getElementsByTagName:', res)
|
||||||
|
if (res === 'script') {
|
||||||
|
return script
|
||||||
|
}
|
||||||
|
if (res === 'meta') {
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
if (res === 'base') {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getElementById: function (res) {
|
||||||
|
console.log('document中的getElementById:', res)
|
||||||
|
if (res === 'root-hammerhead-shadow-ui') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval = function () {}
|
||||||
|
setTimeout = function () {}
|
||||||
|
window.top = window
|
||||||
|
|
||||||
|
'ts_code'
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
cookie = document.cookie.split(';')[0]
|
||||||
|
return cookie
|
||||||
|
}'''
|
||||||
|
|
||||||
|
async def main(timeValue):
|
||||||
|
global runTime, js_codeRead
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
init_result = initCookie()
|
||||||
|
if init_result:
|
||||||
|
cookie = init_result['cookie']
|
||||||
|
execjsRun = init_result['execjsRun']
|
||||||
|
else:
|
||||||
|
print("初始化 cookies 失败")
|
||||||
|
return
|
||||||
|
|
||||||
|
runcookie = {
|
||||||
|
'cookie': cookie,
|
||||||
|
'execjsRun': execjsRun
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加输出 cookies 的代码
|
||||||
|
cookies = {
|
||||||
|
'yiUIIlbdQT3fO': runcookie['cookie'],
|
||||||
|
'yiUIIlbdQT3fP': runcookie['execjsRun'].call('main').split('=')[1]
|
||||||
|
}
|
||||||
|
print(json.dumps(cookies)) # 确保输出是 JSON 格式的
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main(0))
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
# ruishucookie.py
|
||||||
|
import os
|
||||||
|
import execjs
|
||||||
|
import httpx
|
||||||
|
httpx._config.DEFAULT_CIPHERS += ":ALL:@SECLEVEL=1"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
filename = 'Cache.js'
|
||||||
|
if os.path.exists(filename):
|
||||||
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
fileContent = file.read()
|
||||||
|
else:
|
||||||
|
fileContent = ''
|
||||||
|
|
||||||
|
def initCookie(getUrl='https://wapact.189.cn:9001/gateway/standQuery/detailNew/exchange'):
|
||||||
|
global js_code_ym, fileContent
|
||||||
|
cookie = ''
|
||||||
|
response = httpx.post(getUrl)
|
||||||
|
content = response.text.split(' content="')[2].split('" r=')[0]
|
||||||
|
code1 = response.text.split('$_ts=window')[1].split('</script><script type="text/javascript"')[0]
|
||||||
|
code1Content = '$_ts=window' + code1
|
||||||
|
Url = response.text.split('$_ts.lcd();</script><script type="text/javascript" charset="utf-8" src="')[1].split('" r=')[0]
|
||||||
|
urls = getUrl.split('/')
|
||||||
|
rsurl = urls[0] + '//' + urls[2] + Url
|
||||||
|
filename = 'Cache.js'
|
||||||
|
if fileContent == '':
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
fileRes = httpx.get(rsurl)
|
||||||
|
fileContent = fileRes.text
|
||||||
|
if fileRes.status_code == 200:
|
||||||
|
with open(filename, 'w', encoding='utf-8') as file:
|
||||||
|
file.write(fileRes.text)
|
||||||
|
else:
|
||||||
|
print(f"Failed to download {rsurl}. Status code: {fileRes.status_code}")
|
||||||
|
if response.headers['Set-Cookie']:
|
||||||
|
cookie = response.headers['Set-Cookie'].split(';')[0].split('=')[1]
|
||||||
|
runJs = js_code_ym.replace('content_code', content).replace("'ts_code'", code1Content + fileContent)
|
||||||
|
execjsRun = RefererCookie(runJs)
|
||||||
|
|
||||||
|
# 将 cookies 转换为字符串格式
|
||||||
|
cookies_str = f"yiUIIlbdQT3fO={cookie}; yiUIIlbdQT3fP={execjsRun.call('main').split('=')[1]}"
|
||||||
|
return cookies_str
|
||||||
|
|
||||||
|
def RefererCookie(runJs):
|
||||||
|
try:
|
||||||
|
execjsRun = execjs.compile(runJs)
|
||||||
|
return execjsRun
|
||||||
|
except execjs._exceptions.CompileError as e:
|
||||||
|
print(f"JavaScript 编译错误: {e}")
|
||||||
|
except execjs._exceptions.RuntimeError as e:
|
||||||
|
print(f"JavaScript 运行时错误: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"其他错误: {e}")
|
||||||
|
|
||||||
|
js_code_ym = '''delete __filename
|
||||||
|
delete __dirname
|
||||||
|
ActiveXObject = undefined
|
||||||
|
|
||||||
|
window = global;
|
||||||
|
|
||||||
|
content="content_code"
|
||||||
|
|
||||||
|
navigator = {"platform": "Linux aarch64"}
|
||||||
|
navigator = {"userAgent": "CtClient;11.0.0;Android;13;22081212C;NTIyMTcw!#!MTUzNzY"}
|
||||||
|
|
||||||
|
location={
|
||||||
|
"href": "https://",
|
||||||
|
"origin": "",
|
||||||
|
"protocol": "",
|
||||||
|
"host": "",
|
||||||
|
"hostname": "",
|
||||||
|
"port": "",
|
||||||
|
"pathname": "",
|
||||||
|
"search": "",
|
||||||
|
"hash": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i = {length: 0}
|
||||||
|
base = {length: 0}
|
||||||
|
div = {
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('div中的getElementsByTagName:', res)
|
||||||
|
if (res === 'i') {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return '<div></div>'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script = {
|
||||||
|
|
||||||
|
}
|
||||||
|
meta = [
|
||||||
|
{charset:"UTF-8"},
|
||||||
|
{
|
||||||
|
content: content,
|
||||||
|
getAttribute: function (res) {
|
||||||
|
console.log('meta中的getAttribute:', res)
|
||||||
|
if (res === 'r') {
|
||||||
|
return 'm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentNode: {
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('meta中的removeChild:', res)
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
form = '<form></form>'
|
||||||
|
|
||||||
|
window.addEventListener= function (res) {
|
||||||
|
console.log('window中的addEventListener:', res)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
document = {
|
||||||
|
createElement: function (res) {
|
||||||
|
console.log('document中的createElement:', res)
|
||||||
|
|
||||||
|
if (res === 'div') {
|
||||||
|
return div
|
||||||
|
} else if (res === 'form') {
|
||||||
|
return form
|
||||||
|
}
|
||||||
|
else{return res}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
addEventListener: function (res) {
|
||||||
|
console.log('document中的addEventListener:', res)
|
||||||
|
|
||||||
|
},
|
||||||
|
appendChild: function (res) {
|
||||||
|
console.log('document中的appendChild:', res)
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('document中的removeChild:', res)
|
||||||
|
},
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('document中的getElementsByTagName:', res)
|
||||||
|
if (res === 'script') {
|
||||||
|
return script
|
||||||
|
}
|
||||||
|
if (res === 'meta') {
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
if (res === 'base') {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getElementById: function (res) {
|
||||||
|
console.log('document中的getElementById:', res)
|
||||||
|
if (res === 'root-hammerhead-shadow-ui') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval = function () {}
|
||||||
|
setTimeout = function () {}
|
||||||
|
window.top = window
|
||||||
|
|
||||||
|
'ts_code'
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
cookie = document.cookie.split(';')[0]
|
||||||
|
return cookie
|
||||||
|
}'''
|
||||||
|
|
||||||
|
async def main(timeValue):
|
||||||
|
global runTime, js_codeRead
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
init_result = initCookie()
|
||||||
|
if init_result:
|
||||||
|
cookie = init_result['cookie']
|
||||||
|
execjsRun = init_result['execjsRun']
|
||||||
|
else:
|
||||||
|
print("初始化 cookies 失败")
|
||||||
|
return
|
||||||
|
|
||||||
|
runcookie = {
|
||||||
|
'cookie': cookie,
|
||||||
|
'execjsRun': execjsRun
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加输出 cookies 的代码
|
||||||
|
cookies = {
|
||||||
|
'yiUIIlbdQT3fO': runcookie['cookie'],
|
||||||
|
'yiUIIlbdQT3fP': runcookie['execjsRun'].call('main').split('=')[1]
|
||||||
|
}
|
||||||
|
print(f"Cookies: {cookies}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main(0))
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
const $ = new Env("瑞数通杀");
|
||||||
|
|
||||||
|
*/
|
||||||
|
delete __filename
|
||||||
|
delete __dirname
|
||||||
|
ActiveXObject = undefined
|
||||||
|
|
||||||
|
window = global;
|
||||||
|
|
||||||
|
|
||||||
|
content="content_code"
|
||||||
|
|
||||||
|
|
||||||
|
navigator = {"platform": "Linux aarch64"}
|
||||||
|
navigator = {"userAgent": "CtClient;11.0.0;Android;13;22081212C;NTIyMTcw!#!MTUzNzY"}
|
||||||
|
|
||||||
|
location={
|
||||||
|
"href": "https://",
|
||||||
|
"origin": "",
|
||||||
|
"protocol": "",
|
||||||
|
"host": "",
|
||||||
|
"hostname": "",
|
||||||
|
"port": "",
|
||||||
|
"pathname": "",
|
||||||
|
"search": "",
|
||||||
|
"hash": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i = {length: 0}
|
||||||
|
base = {length: 0}
|
||||||
|
div = {
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('div中的getElementsByTagName:', res)
|
||||||
|
if (res === 'i') {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return '<div></div>'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script = {
|
||||||
|
|
||||||
|
}
|
||||||
|
meta = [
|
||||||
|
{charset:"UTF-8"},
|
||||||
|
{
|
||||||
|
content: content,
|
||||||
|
getAttribute: function (res) {
|
||||||
|
console.log('meta中的getAttribute:', res)
|
||||||
|
if (res === 'r') {
|
||||||
|
return 'm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentNode: {
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('meta中的removeChild:', res)
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
form = '<form></form>'
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener= function (res) {
|
||||||
|
console.log('window中的addEventListener:', res)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document = {
|
||||||
|
|
||||||
|
|
||||||
|
createElement: function (res) {
|
||||||
|
console.log('document中的createElement:', res)
|
||||||
|
|
||||||
|
|
||||||
|
if (res === 'div') {
|
||||||
|
return div
|
||||||
|
} else if (res === 'form') {
|
||||||
|
return form
|
||||||
|
}
|
||||||
|
else{return res}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
addEventListener: function (res) {
|
||||||
|
console.log('document中的addEventListener:', res)
|
||||||
|
|
||||||
|
},
|
||||||
|
appendChild: function (res) {
|
||||||
|
console.log('document中的appendChild:', res)
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
removeChild: function (res) {
|
||||||
|
console.log('document中的removeChild:', res)
|
||||||
|
},
|
||||||
|
getElementsByTagName: function (res) {
|
||||||
|
console.log('document中的getElementsByTagName:', res)
|
||||||
|
if (res === 'script') {
|
||||||
|
return script
|
||||||
|
}
|
||||||
|
if (res === 'meta') {
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
if (res === 'base') {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getElementById: function (res) {
|
||||||
|
console.log('document中的getElementById:', res)
|
||||||
|
if (res === 'root-hammerhead-shadow-ui') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval = function () {}
|
||||||
|
setTimeout = function () {}
|
||||||
|
window.top = window
|
||||||
|
|
||||||
|
|
||||||
|
'ts_code'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
cookie = document.cookie.split(';')[0]
|
||||||
|
return cookie
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,632 @@
|
|||||||
|
#获取AndroidID地址:https://commissions-yields-exception-personally.trycloudflare.com
|
||||||
|
#变量dxqy=账户@密码@AndroidID 多账户&隔开
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import random
|
||||||
|
import certifi
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
import binascii
|
||||||
|
import traceback
|
||||||
|
import subprocess
|
||||||
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
from http import cookiejar
|
||||||
|
from threading import Event as ThreadingEvent, Lock as ThreadingLock
|
||||||
|
from asyncio import Event as AsyncioEvent
|
||||||
|
from Crypto.Cipher import DES3
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Cipher import PKCS1_v1_5
|
||||||
|
from Crypto.Util.Padding import pad, unpad
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, wait
|
||||||
|
|
||||||
|
# --- 基础设置和辅助函数 ---
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
context.set_ciphers('DEFAULT@SECLEVEL=1')
|
||||||
|
context.check_hostname = False
|
||||||
|
context.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
class DESAdapter(requests.adapters.HTTPAdapter):
|
||||||
|
def init_poolmanager(self, *args, **kwargs):
|
||||||
|
kwargs['ssl_context'] = context
|
||||||
|
return super().init_poolmanager(*args, **kwargs)
|
||||||
|
|
||||||
|
class BlockAll(cookiejar.CookiePolicy):
|
||||||
|
return_ok = set_ok = domain_return_ok = path_return_ok = lambda self, *args, **kwargs: False
|
||||||
|
netscape = True
|
||||||
|
rfc2965 = hide_cookie2 = False
|
||||||
|
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
def printn(m):
|
||||||
|
current_time = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||||
|
print(f'\n[{current_time}] {m}')
|
||||||
|
|
||||||
|
# --- 加密/解密/工具函数(保持不变)---
|
||||||
|
key = b'1234567`90koiuyhgtfrdews'
|
||||||
|
iv = 8 * b'\0'
|
||||||
|
|
||||||
|
public_key_b64 = '''-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBkLT15ThVgz6/NOl6s8GNPofdWzWbCkWnkaAm7O2LjkM1H7dMvzkiqdxU02jamGRHLX/ZNMCXHnPcW/sDhiFCBN18qFvy8g6VYb9QtroI09e176s+ZCtiv7hbin2cCTj99iUpnEloZm19lwHyo69u5UMiPMpq0/XKBO8lYhN/gwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----'''
|
||||||
|
|
||||||
|
public_key_data = '''-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+ugG5A8cZ3FqUKDwM57GM4io6JGcStivT8UdGt67PEOihLZTw3P7371+N47PrmsCpnTRzbTgcupKtUv8ImZalYk65dU8rjC/ridwhw9ffW2LBwvkEnDkkKKRi2liWIItDftJVBiWOh17o6gfbPoNrWORcAdcbpk2L+udld5kZNwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----'''
|
||||||
|
|
||||||
|
def encrypt_des3(text):
|
||||||
|
cipher = DES3.new(key, DES3.MODE_CBC, iv)
|
||||||
|
return cipher.encrypt(pad(text.encode(), DES3.block_size)).hex()
|
||||||
|
|
||||||
|
def decrypt_des3(text):
|
||||||
|
cipher = DES3.new(key, DES3.MODE_CBC, iv)
|
||||||
|
return unpad(cipher.decrypt(bytes.fromhex(text)), DES3.block_size).decode()
|
||||||
|
|
||||||
|
def b64_encrypt_rsa(plaintext):
|
||||||
|
public_key = RSA.import_key(public_key_b64)
|
||||||
|
cipher = PKCS1_v1_5.new(public_key)
|
||||||
|
return base64.b64encode(cipher.encrypt(plaintext.encode())).decode()
|
||||||
|
|
||||||
|
def encrypt_para_rsa_new(p):
|
||||||
|
k = RSA.import_key(public_key_data)
|
||||||
|
c = PKCS1_v1_5.new(k)
|
||||||
|
s = k.size_in_bytes() - 11
|
||||||
|
d = p.encode() if isinstance(p, str) else json.dumps(p).encode()
|
||||||
|
return binascii.hexlify(b''.join(c.encrypt(d[i:i+s]) for i in range(0, len(d), s))).decode()
|
||||||
|
|
||||||
|
def encode_phone(text):
|
||||||
|
return ''.join([chr(ord(char) + 2) for char in text])
|
||||||
|
|
||||||
|
def get_ruishu_cookies():
|
||||||
|
try:
|
||||||
|
ruishu_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Ruishu.py')
|
||||||
|
if not os.path.exists(ruishu_script_path):
|
||||||
|
print(f"❌ 错误: 未找到 {ruishu_script_path} 文件。")
|
||||||
|
return None
|
||||||
|
result = subprocess.run([sys.executable, ruishu_script_path], capture_output=True, text=True, encoding='utf-8', check=True)
|
||||||
|
return json.loads(result.stdout.strip())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 获取瑞数Cookie时发生错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# --- 推送与持久化函数 ---
|
||||||
|
def send_pushplus_notification(token, title, content):
|
||||||
|
if not token:
|
||||||
|
printn("ℹ️ 未配置PUSH_PLUS_TOKEN,跳过推送。")
|
||||||
|
return
|
||||||
|
url = "http://www.pushplus.plus/send"
|
||||||
|
payload = {"token": token, "title": title, "content": content, "template": "markdown"}
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload)
|
||||||
|
if response.json().get("code") == 200:
|
||||||
|
printn("✅ PUSHPLUS 推送成功!")
|
||||||
|
else:
|
||||||
|
printn(f"❌ PUSHPLUS 推送失败: {response.json().get('msg')}")
|
||||||
|
except Exception as e:
|
||||||
|
printn(f"💥 推送时发生异常: {e}")
|
||||||
|
|
||||||
|
def load_claimed_accounts(filename):
|
||||||
|
try:
|
||||||
|
if os.path.exists(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (json.JSONDecodeError, IOError):
|
||||||
|
pass
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def save_claimed_account(filename, phone, lock):
|
||||||
|
with lock:
|
||||||
|
claimed_data = load_claimed_accounts(filename)
|
||||||
|
current_month = datetime.datetime.now().strftime("%Y-%m")
|
||||||
|
claimed_data[phone] = current_month
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
json.dump(claimed_data, f, indent=4)
|
||||||
|
|
||||||
|
# =================== 新登录方法 login_v2 (Android) ===================
|
||||||
|
def login_v2(phone: str, password: str, android_id: str, session: requests.Session):
|
||||||
|
"""
|
||||||
|
使用 Android 设备标识登录,返回包含 uid(token) 的用户信息字典
|
||||||
|
"""
|
||||||
|
m_phone = phone[:3] + '****' + phone[-4:] if len(phone) >= 7 else phone
|
||||||
|
printn(f"[登录v2] {m_phone} 开始登录 (AndroidId={android_id[:6]}...)")
|
||||||
|
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
# 随机生成设备UUID段
|
||||||
|
alphabet = 'abcdef0123456789'
|
||||||
|
uuid = [
|
||||||
|
''.join(random.sample(alphabet, 8)),
|
||||||
|
''.join(random.sample(alphabet, 4)),
|
||||||
|
'4' + ''.join(random.sample(alphabet, 3)),
|
||||||
|
''.join(random.sample(alphabet, 4)),
|
||||||
|
''.join(random.sample(alphabet, 12))
|
||||||
|
]
|
||||||
|
device_part = uuid[0] + uuid[1] + uuid[2] # 用于deviceUid
|
||||||
|
|
||||||
|
loginAuthCipherAsymmertric = f"Xiaomi 20 8.0.0.{android_id[:12]}{phone}{timestamp}{password}0$$$0."
|
||||||
|
body = {
|
||||||
|
"headerInfos": {
|
||||||
|
"code": "userLoginNormal",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"broadAccount": "",
|
||||||
|
"broadToken": "",
|
||||||
|
"clientType": "#11.0.0#channel8#Xiaomi 20#",
|
||||||
|
"shopId": "20002",
|
||||||
|
"source": "110003",
|
||||||
|
"sourcePassword": "Sid98s",
|
||||||
|
"token": "",
|
||||||
|
"userLoginName": encode_phone(phone)
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"attach": "test",
|
||||||
|
"fieldData": {
|
||||||
|
"loginType": "4",
|
||||||
|
"accountType": "",
|
||||||
|
"loginAuthCipherAsymmertric": b64_encrypt_rsa(loginAuthCipherAsymmertric),
|
||||||
|
"deviceUid": "",
|
||||||
|
"phoneNum": encode_phone(phone),
|
||||||
|
"isChinatelecom": "",
|
||||||
|
"systemVersion": "8.0.0",
|
||||||
|
"androidId": encode_phone(android_id),
|
||||||
|
"loginAuthCipher": "",
|
||||||
|
"authentication": encode_phone(password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = session.post(
|
||||||
|
'https://appgologin.189.cn:9031/login/client/userLoginNormal',
|
||||||
|
json=body,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
printn(f"[登录v2失败] {m_phone} HTTP状态码 {resp.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = resp.json()
|
||||||
|
login_data = res.get('responseData', {}).get('data', {}).get('loginSuccessResult')
|
||||||
|
if not login_data:
|
||||||
|
err_msg = res.get('responseData', {}).get('data', {}).get('resultMsg') or '无loginSuccessResult'
|
||||||
|
printn(f"[登录v2失败] {m_phone}: {err_msg}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 第二步:获取Ticket
|
||||||
|
xml = f'''<Request>
|
||||||
|
<HeaderInfos>
|
||||||
|
<Code>getSingle</Code>
|
||||||
|
<Timestamp>{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}</Timestamp>
|
||||||
|
<BroadAccount></BroadAccount>
|
||||||
|
<BroadToken></BroadToken>
|
||||||
|
<ClientType>#9.6.1#channel50#iPhone 14 Pro Max#</ClientType>
|
||||||
|
<ShopId>20002</ShopId>
|
||||||
|
<Source>110003</Source>
|
||||||
|
<SourcePassword>Sid98s</SourcePassword>
|
||||||
|
<Token>{login_data["token"]}</Token>
|
||||||
|
<UserLoginName>{phone}</UserLoginName>
|
||||||
|
</HeaderInfos>
|
||||||
|
<Content>
|
||||||
|
<Attach>test</Attach>
|
||||||
|
<FieldData>
|
||||||
|
<TargetId>{encrypt_des3(login_data["userId"])}</TargetId>
|
||||||
|
<Url>4a6862274835b451</Url>
|
||||||
|
</FieldData>
|
||||||
|
</Content>
|
||||||
|
</Request>'''
|
||||||
|
xml_resp = session.post(
|
||||||
|
'https://appgologin.189.cn:9031/map/clientXML',
|
||||||
|
data=xml,
|
||||||
|
headers={'Content-Type': 'application/xml'},
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
if xml_resp.status_code != 200:
|
||||||
|
printn(f"[获取Ticket失败] {m_phone} 状态码 {xml_resp.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
xml_text = xml_resp.text
|
||||||
|
if '过期' in xml_text or '校验错误' in xml_text:
|
||||||
|
printn(f"[获取Ticket失败] {m_phone}: {xml_text[:50]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if '<Ticket>' not in xml_text:
|
||||||
|
printn(f"[Ticket异常] {m_phone} 响应无Ticket标签")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
ticket = xml_text.split('<Ticket>')[1].split('</Ticket>')[0]
|
||||||
|
uid = decrypt_des3(ticket)
|
||||||
|
except Exception as e:
|
||||||
|
printn(f"[解析Ticket失败] {m_phone}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 第三步:统一登录获取Bearer(可选,后续可能用到)
|
||||||
|
# 注意:原抢购逻辑不依赖Bearer,仅需ticket,但这里保留获取
|
||||||
|
auth_body = json.dumps({"ticket": uid, "backUrl": "https%3A%2F%2Fwapact.189.cn%3A9001", "platformCode": "P201010301", "loginType": 2})
|
||||||
|
# 使用AES加密(原脚本未定义,但此处不需要,因为原抢购流程使用ticket即可)
|
||||||
|
# 原脚本中的getSign使用ticket,所以无需Bearer,注释掉统一登录步骤
|
||||||
|
|
||||||
|
user_info = {
|
||||||
|
**login_data,
|
||||||
|
'uid': uid,
|
||||||
|
'phoneNbr': phone,
|
||||||
|
'ticket': uid # 兼容后续
|
||||||
|
}
|
||||||
|
printn(f"[登录v2成功] {m_phone} 获取ticket成功")
|
||||||
|
return user_info
|
||||||
|
except Exception as e:
|
||||||
|
printn(f"[登录v2异常] {m_phone}: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
return None
|
||||||
|
|
||||||
|
# =================== 原抢购相关函数(仅替换登录调用) ===================
|
||||||
|
|
||||||
|
def getSign(ticket, session, rs_cookies):
|
||||||
|
try:
|
||||||
|
response = session.get(
|
||||||
|
f'https://wappark.189.cn/jt-sign/ssoHomLogin?ticket={ticket}',
|
||||||
|
cookies=rs_cookies,
|
||||||
|
headers={
|
||||||
|
'User-Agent': "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
json_data = response.json()
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
json_data = {}
|
||||||
|
if json_data.get('resoultCode') == '0':
|
||||||
|
return json_data.get('sign'), json_data.get('accId')
|
||||||
|
else:
|
||||||
|
print(f"❌ 获取sign失败: {json_data}")
|
||||||
|
return None, None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ getSign 异常: {e}")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def getLevelRightsList(phone, accId, session):
|
||||||
|
try:
|
||||||
|
value = {"type": "hg_qd_djqydh", "accId": accId, "shopId": "20001"}
|
||||||
|
paraV = encrypt_para_rsa_new(value)
|
||||||
|
response = session.post(
|
||||||
|
'https://wappark.189.cn/jt-sign/paradise/queryLevelRightInfo',
|
||||||
|
json={"para": paraV}
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
data = response.json()
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
data = {}
|
||||||
|
if data.get('code') == 401:
|
||||||
|
print(f"❌ {phone} 获取权益列表失败: {data}")
|
||||||
|
return None
|
||||||
|
key_name = f"V{data['currentLevel']}"
|
||||||
|
ids = [item['activityId'] for item in data.get(key_name, []) if '话费' in item.get('title', '')]
|
||||||
|
return ids
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ getLevelRightsList 异常: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# === 异步抢购函数(不变) ===
|
||||||
|
async def async_staggered_burst_worker(
|
||||||
|
session, phone, rightsId, accId, sign, rs_cookies,
|
||||||
|
global_stop_event, local_stop_event,
|
||||||
|
task_index, base_target_time,
|
||||||
|
result_log, result_lock, file_lock,
|
||||||
|
num_accounts_to_run
|
||||||
|
):
|
||||||
|
fire_time = base_target_time + datetime.timedelta(milliseconds=(interval * task_index))
|
||||||
|
wait_seconds = (fire_time - datetime.datetime.now()).total_seconds()
|
||||||
|
if wait_seconds > 0 and not debug:
|
||||||
|
await asyncio.sleep(wait_seconds)
|
||||||
|
|
||||||
|
if local_stop_event.is_set() or global_stop_event.is_set():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
value = {"id": rightsId, "accId": accId, "showType": "9003", "showEffect": "8", "czValue": "0"}
|
||||||
|
paraV = encrypt_para_rsa_new(value)
|
||||||
|
headers = {
|
||||||
|
"sign": sign,
|
||||||
|
"Referer": "https://wappark.189.cn/resources/dist/signInActivity.html",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36"
|
||||||
|
}
|
||||||
|
|
||||||
|
url = "https://wappark.189.cn/jt-sign/paradise/receiverRights"
|
||||||
|
|
||||||
|
async with session.post(url, json={"para": paraV}, cookies=rs_cookies, headers=headers) as response:
|
||||||
|
request_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
|
||||||
|
|
||||||
|
text = ""
|
||||||
|
try:
|
||||||
|
text = await response.text(encoding='utf-8', errors='replace')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
printn(f"⏰【{phone}】@{request_time} [任务{task_index}] 响应读取超时")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
text = f"[响应读取异常: {type(e).__name__} - {str(e)}]"
|
||||||
|
|
||||||
|
res_json = {}
|
||||||
|
res_text = ""
|
||||||
|
try:
|
||||||
|
clean_text = text.strip()
|
||||||
|
if clean_text.startswith('\ufeff'):
|
||||||
|
clean_text = clean_text[1:]
|
||||||
|
if clean_text:
|
||||||
|
res_json = json.loads(clean_text)
|
||||||
|
res_text = json.dumps(res_json, ensure_ascii=False)
|
||||||
|
else:
|
||||||
|
res_text = "[空响应]"
|
||||||
|
except (json.JSONDecodeError, ValueError, TypeError):
|
||||||
|
res_text = f"非JSON响应: {text[:100]}"
|
||||||
|
|
||||||
|
if "已领完" in res_text or "活动已结束" in res_text:
|
||||||
|
printn(f"💨【{phone}】@{request_time} [任务{task_index}] 已售罄! 停止该账号后续请求。")
|
||||||
|
if not local_stop_event.is_set():
|
||||||
|
local_stop_event.set()
|
||||||
|
with result_lock:
|
||||||
|
if phone not in result_log or result_log.get(phone, {}).get('status') != 'SUCCESS':
|
||||||
|
result_log[phone] = {'status': 'SOLD_OUT', 'message': '已售罄'}
|
||||||
|
finished_count = len([r for r in result_log.values() if r.get('status') in ('SUCCESS', 'SOLD_OUT')])
|
||||||
|
has_success = any(res.get('status') == 'SUCCESS' for res in result_log.values())
|
||||||
|
if not has_success and finished_count == num_accounts_to_run and not global_stop_event.is_set():
|
||||||
|
global_stop_event.set()
|
||||||
|
printn(f"🛑【全局共识】所有账号均确认售罄或失败,触发全局停止信号!")
|
||||||
|
|
||||||
|
elif "成功" in res_text or "已领取过该权益" in res_text:
|
||||||
|
printn(f"🎉【{phone}】@{request_time} [任务{task_index}] 成功或已领取!")
|
||||||
|
if not local_stop_event.is_set():
|
||||||
|
local_stop_event.set()
|
||||||
|
printn(f"🛑【{phone}】个人停止信号已发出 (原因: 成功)。")
|
||||||
|
with result_lock:
|
||||||
|
result_log[phone] = {'status': 'SUCCESS', 'message': res_json.get('resoultMsg', '成功/已领取')}
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
await loop.run_in_executor(None, save_claimed_account, claimed_log_file, phone, file_lock)
|
||||||
|
|
||||||
|
elif "当前抢购人数过多" in res_text:
|
||||||
|
printn(f"👥【{phone}】@{request_time} [任务{task_index}] 人数过多,继续尝试...")
|
||||||
|
|
||||||
|
else:
|
||||||
|
printn(f"💬【{phone}】@{request_time} [任务{task_index}] 响应: {res_text}")
|
||||||
|
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
request_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
|
||||||
|
printn(f"⏰【{phone}】@{request_time} [任务{task_index}] 请求超时 (连接或响应)")
|
||||||
|
except Exception as e:
|
||||||
|
request_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
|
||||||
|
printn(f"🚨【{phone}】@{request_time} [任务{task_index}] 未预期异常: {e.__class__.__name__} - {str(e)}")
|
||||||
|
|
||||||
|
def run_attack_campaign(phone, ticket, ss, global_stop_event, enable_ruishu, result_log, result_lock, file_lock, num_accounts_to_run):
|
||||||
|
try:
|
||||||
|
if global_stop_event.is_set():
|
||||||
|
return
|
||||||
|
printn(f"⚙️【{phone}】开始准备凭证 (同步模式)...")
|
||||||
|
rs_cookies = get_ruishu_cookies() if enable_ruishu else {}
|
||||||
|
if enable_ruishu and not rs_cookies:
|
||||||
|
raise Exception("瑞数已启用但获取Cookie失败")
|
||||||
|
|
||||||
|
sign, accId = getSign(ticket, ss, rs_cookies)
|
||||||
|
if not sign:
|
||||||
|
raise Exception("获取Sign失败")
|
||||||
|
|
||||||
|
ss.headers.update({
|
||||||
|
"sign": sign,
|
||||||
|
"Referer": "https://wappark.189.cn/resources/dist/signInActivity.html"
|
||||||
|
})
|
||||||
|
rightsIds = getLevelRightsList(phone, accId, ss)
|
||||||
|
if not rightsIds:
|
||||||
|
raise Exception("未能获取到权益ID")
|
||||||
|
|
||||||
|
rightsId = rightsIds[0]
|
||||||
|
printn(f"✅【{phone}】凭证准备就绪,切换至异步并发抢购...")
|
||||||
|
|
||||||
|
asyncio.run(run_async_bursts(phone, rightsId, accId, sign, rs_cookies, global_stop_event, result_log, result_lock, file_lock, num_accounts_to_run))
|
||||||
|
|
||||||
|
with result_lock:
|
||||||
|
if phone not in result_log:
|
||||||
|
result_log[phone] = {'status': 'UNKNOWN', 'message': '抢购结束但未记录明确状态'}
|
||||||
|
printn(f"🏁【{phone}】抢购任务已结束。")
|
||||||
|
except Exception as e:
|
||||||
|
printn(f"💥【{phone}】准备或执行阶段出现严重异常: {e}")
|
||||||
|
with result_lock:
|
||||||
|
result_log[phone] = {'status': 'FAIL', 'message': str(e)}
|
||||||
|
|
||||||
|
async def run_async_bursts(phone, rightsId, accId, sign, rs_cookies, global_stop_event, result_log, result_lock, file_lock, num_accounts_to_run):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
target_time = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
|
||||||
|
if now >= target_time:
|
||||||
|
target_time += datetime.timedelta(days=1)
|
||||||
|
base_target_time = target_time + datetime.timedelta(milliseconds=inadvance)
|
||||||
|
|
||||||
|
local_stop_event = AsyncioEvent()
|
||||||
|
connector = aiohttp.TCPConnector(ssl=ssl.create_default_context(cafile=certifi.where()))
|
||||||
|
timeout = aiohttp.ClientTimeout(total=15, connect=10)
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession(connector=connector, timeout=timeout) as async_session:
|
||||||
|
tasks = [
|
||||||
|
async_staggered_burst_worker(
|
||||||
|
async_session, phone, rightsId, accId, sign, rs_cookies,
|
||||||
|
global_stop_event, local_stop_event, i, base_target_time,
|
||||||
|
result_log, result_lock, file_lock, num_accounts_to_run
|
||||||
|
)
|
||||||
|
for i in range(count_per_account)
|
||||||
|
]
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
# =================== 修改后的账号处理函数 ===================
|
||||||
|
def process_account(account_str, global_stop_event, enable_ruishu, result_log, result_lock, file_lock, num_accounts_to_run):
|
||||||
|
"""
|
||||||
|
账号格式:手机号@密码@AndroidID
|
||||||
|
"""
|
||||||
|
parts = account_str.split('@')
|
||||||
|
if len(parts) < 3:
|
||||||
|
printn(f"⚠️ 账号格式错误:缺少AndroidID,应为 '手机号@密码@AndroidID',实际为 '{account_str}',跳过该账号。")
|
||||||
|
# 记录失败状态
|
||||||
|
with result_lock:
|
||||||
|
phone = parts[0] if parts else 'unknown'
|
||||||
|
result_log[phone] = {'status': 'LOGIN_FAIL', 'message': '缺少AndroidID'}
|
||||||
|
return
|
||||||
|
|
||||||
|
phone, password, android_id = parts[0], parts[1], parts[2]
|
||||||
|
if not android_id.strip():
|
||||||
|
printn(f"⚠️ 账号 {phone} 的AndroidID为空,请填写有效的AndroidID(从小程序“脚本置顶”获取),跳过该账号。")
|
||||||
|
with result_lock:
|
||||||
|
result_log[phone] = {'status': 'LOGIN_FAIL', 'message': 'AndroidID为空'}
|
||||||
|
return
|
||||||
|
|
||||||
|
if not debug:
|
||||||
|
delay = random.uniform(0.1, 2.0)
|
||||||
|
time.sleep(delay)
|
||||||
|
printn(f'👤【{phone}】(延迟{delay:.2f}s后) 开始登录...')
|
||||||
|
else:
|
||||||
|
printn(f'👤【{phone}】开始登录...')
|
||||||
|
|
||||||
|
# 创建会话
|
||||||
|
ss = requests.session()
|
||||||
|
ss.headers = {
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36"
|
||||||
|
}
|
||||||
|
ss.mount('https://', DESAdapter())
|
||||||
|
ss.cookies.set_policy(BlockAll())
|
||||||
|
ss.timeout = 30
|
||||||
|
|
||||||
|
# 使用 login_v2 登录
|
||||||
|
user_info = login_v2(phone, password, android_id, ss)
|
||||||
|
if user_info:
|
||||||
|
ticket = user_info['uid'] # 即 ticket
|
||||||
|
run_attack_campaign(phone, ticket, ss, global_stop_event, enable_ruishu, result_log, result_lock, file_lock, num_accounts_to_run)
|
||||||
|
else:
|
||||||
|
printn(f'❌【{phone}】登录失败')
|
||||||
|
with result_lock:
|
||||||
|
result_log[phone] = {'status': 'LOGIN_FAIL', 'message': '登录失败'}
|
||||||
|
|
||||||
|
# =================== 主函数 ===================
|
||||||
|
def main():
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
PHONES = os.environ.get('dxqy')
|
||||||
|
push_plus_token = os.environ.get('PUSH_PLUS_TOKEN')
|
||||||
|
if not PHONES:
|
||||||
|
printn("ℹ️ 未检测到环境变量 `dxqy`,将使用脚本内嵌的账号信息。")
|
||||||
|
PHONES = "你的手机号@你的服务密码@你的AndroidID" # 务必填写完整格式,AndroidID请从小程序“脚本置顶”中获取
|
||||||
|
all_accounts = [p.strip() for p in PHONES.split('&') if p.strip() and '@' in p and "你的手机号" not in p]
|
||||||
|
if not all_accounts:
|
||||||
|
printn("❌ 请在环境变量或脚本中设置正确的账号信息(格式:手机号@密码@AndroidID,AndroidID请从小程序“脚本置顶”中获取)。")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查每个账号是否有AndroidID
|
||||||
|
valid_accounts = []
|
||||||
|
for acc in all_accounts:
|
||||||
|
parts = acc.split('@')
|
||||||
|
if len(parts) < 3 or not parts[2].strip():
|
||||||
|
printn(f"⚠️ 账号 '{acc}' 缺少AndroidID,将被跳过。请补充完整格式:手机号@密码@AndroidID,缺少AndroidID,格式应为 手机号@密码@AndroidID,AndroidID请从小程序“脚本置顶”中获取。跳过该账号")
|
||||||
|
continue
|
||||||
|
valid_accounts.append(acc)
|
||||||
|
|
||||||
|
if not valid_accounts:
|
||||||
|
printn("❌ 没有有效的账号(均缺少AndroidID),缺少AndroidID,格式应为 手机号@密码@AndroidID,AndroidID请从小程序“脚本置顶”中获取。跳过该账号。")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 加载已领取记录
|
||||||
|
claimed_data = load_claimed_accounts(claimed_log_file)
|
||||||
|
current_month = datetime.datetime.now().strftime("%Y-%m")
|
||||||
|
accounts_to_run = []
|
||||||
|
skipped_accounts = []
|
||||||
|
for acc in valid_accounts:
|
||||||
|
phone = acc.split('@')[0]
|
||||||
|
if claimed_data.get(phone) == current_month:
|
||||||
|
skipped_accounts.append(f"✅ {phone[:3]}***{phone[-4:]}: 本月已领取")
|
||||||
|
else:
|
||||||
|
accounts_to_run.append(acc)
|
||||||
|
|
||||||
|
printn("="*20 + " 账号过滤 " + "="*20)
|
||||||
|
print(f"总有效账号数: {len(valid_accounts)}, 本次运行: {len(accounts_to_run)}, 本月已领取跳过: {len(skipped_accounts)}")
|
||||||
|
for s in skipped_accounts:
|
||||||
|
print(s)
|
||||||
|
printn("="*52)
|
||||||
|
if not accounts_to_run:
|
||||||
|
printn("🏁 所有账号本月均已领取,任务结束!")
|
||||||
|
return
|
||||||
|
|
||||||
|
global_stop_event = ThreadingEvent()
|
||||||
|
result_log = {}
|
||||||
|
result_lock = ThreadingLock()
|
||||||
|
file_lock = ThreadingLock()
|
||||||
|
num_accounts_to_run = len(accounts_to_run)
|
||||||
|
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
prepare_time = now.replace(hour=23, minute=59, second=0, microsecond=0)
|
||||||
|
if now >= prepare_time:
|
||||||
|
prepare_time += datetime.timedelta(days=1)
|
||||||
|
wait_seconds = (prepare_time - now).total_seconds()
|
||||||
|
if wait_seconds > 0 and not debug:
|
||||||
|
printn(f"⏳ 主程序等待 {wait_seconds:.0f} 秒,将在 {prepare_time.strftime('%H:%M:%S')} 开始并行登录...")
|
||||||
|
time.sleep(wait_seconds)
|
||||||
|
elif debug:
|
||||||
|
printn(f"🐛 [DEBUG模式] 跳过主程序等待,立即开始登录...")
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=len(accounts_to_run)) as executor:
|
||||||
|
futures = [
|
||||||
|
executor.submit(
|
||||||
|
process_account, acc, global_stop_event, ENABLE_RUISHU,
|
||||||
|
result_log, result_lock, file_lock, num_accounts_to_run
|
||||||
|
)
|
||||||
|
for acc in accounts_to_run
|
||||||
|
]
|
||||||
|
wait(futures)
|
||||||
|
|
||||||
|
end_time = datetime.datetime.now()
|
||||||
|
duration = (end_time - start_time).total_seconds()
|
||||||
|
|
||||||
|
summary_title = f"电信权益抢购总结 ({end_time.strftime('%Y-%m-%d %H:%M')})"
|
||||||
|
success_accounts = skipped_accounts.copy()
|
||||||
|
fail_accounts = []
|
||||||
|
|
||||||
|
final_state = '全部完成'
|
||||||
|
if global_stop_event.is_set():
|
||||||
|
final_state = '已售罄 (全局共识)'
|
||||||
|
|
||||||
|
for acc in accounts_to_run:
|
||||||
|
phone = acc.split('@')[0]
|
||||||
|
res = result_log.get(phone)
|
||||||
|
if res:
|
||||||
|
if res['status'] == 'SUCCESS':
|
||||||
|
success_accounts.append(f"🎉 {phone[:3]}***{phone[-4:]}: {res['message']}")
|
||||||
|
else:
|
||||||
|
fail_accounts.append(f"❌ {phone[:3]}***{phone[-4:]}: {res['message']}")
|
||||||
|
else:
|
||||||
|
fail_accounts.append(f"ℹ️ {phone[:3]}***{phone[-4:]}: 未执行或无明确结果")
|
||||||
|
|
||||||
|
summary_content = (f"### 任务报告\n- **总耗时**: {duration:.2f} 秒\n- **最终状态**: {final_state}\n---\n### 成功/已领取列表 ({len(success_accounts)}/{len(valid_accounts)})\n")
|
||||||
|
summary_content += "\n".join(success_accounts) if success_accounts else "无"
|
||||||
|
summary_content += f"\n\n### 失败/未成功列表 ({len(fail_accounts)}/{len(valid_accounts)})\n"
|
||||||
|
summary_content += "\n".join(fail_accounts) if fail_accounts else "无"
|
||||||
|
|
||||||
|
printn("="*22 + " 抢购总结 " + "="*22)
|
||||||
|
print(summary_content)
|
||||||
|
printn("="*52)
|
||||||
|
send_pushplus_notification(push_plus_token, summary_title, summary_content)
|
||||||
|
printn("🏁 所有账号的任务均已结束!")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
inadvance = -100 # 提前100毫秒(可调)
|
||||||
|
count_per_account = 2 # 每账号抢购次数
|
||||||
|
interval = 1 # 请求间隔(毫秒)
|
||||||
|
hour = 0
|
||||||
|
minute = 0
|
||||||
|
debug = False # 测试时改为True
|
||||||
|
ENABLE_RUISHU = False
|
||||||
|
claimed_log_file = "claimed_accounts.json"
|
||||||
|
|
||||||
|
print("="*52)
|
||||||
|
print(" 电信等级会员权益兑换(终极加固版)")
|
||||||
|
print("="*52)
|
||||||
|
print(f"🕒 目标时间: {hour:02d}:{minute:02d} | 🎯 每号抢购数: {count_per_account} | 💥 抢购间隔: {interval}ms")
|
||||||
|
print(f"⚡️ 首发提前: {-inadvance}ms")
|
||||||
|
print(f"🐞 Debug模式: {'开启' if debug else '关闭'}")
|
||||||
|
print(f"🤖 瑞数Cookie: {'启用' if ENABLE_RUISHU else '禁用'}")
|
||||||
|
print(f"📓 领取记录文件: {claimed_log_file}")
|
||||||
|
print("="*52)
|
||||||
|
main()
|
||||||
@@ -0,0 +1,374 @@
|
|||||||
|
#获取AndroidID地址:https://commissions-yields-exception-personally.trycloudflare.com
|
||||||
|
#变量dxlin=手机号#密码#AndroidID
|
||||||
|
import os, sys, json, time, random, string, base64, asyncio, certifi, requests
|
||||||
|
from typing import Dict, Any, Union
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Cipher import PKCS1_v1_5, DES3, AES
|
||||||
|
from Crypto.Util.Padding import pad, unpad
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from urllib3.util.ssl_ import create_urllib3_context
|
||||||
|
|
||||||
|
# 尝试导入通知模块,若不存在则忽略
|
||||||
|
try:
|
||||||
|
import notify
|
||||||
|
HAS_NOTIFY = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_NOTIFY = False
|
||||||
|
notify = None
|
||||||
|
|
||||||
|
# --- 配置与常量 ---
|
||||||
|
KEYS = {
|
||||||
|
'login_rsa': """-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBkLT15ThVgz6/NOl6s8GNPofdWzWbCkWnkaAm7O2LjkM1H7dMvzkiqdxU02jamGRHLX/ZNMCXHnPcW/sDhiFCBN18qFvy8g6VYb9QtroI09e176s+ZCtiv7hbin2cCTj99iUpnEloZm19lwHyo69u5UMiPMpq0/XKBO8lYhN/gwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----""",
|
||||||
|
'data_rsa': """-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+ugG5A8cZ3FqUKDwM57GM4io6JGcStivT8UdGt67PEOihLZTw3P7371+N47PrmsCpnTRzbTgcupKtUv8ImZalYk65dU8rjC/ridwhw9ffW2LBwvkEnDkkKKRi2liWIItDftJVBiWOh17o6gfbPoNrWORcAdcbpk2L+udld5kZNwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----""",
|
||||||
|
'des3': b'1234567`90koiuyhgtfrdews',
|
||||||
|
'aes_def': b'34d7cb0bcdf07523',
|
||||||
|
'aes_login': 'telecom_wap_2018'
|
||||||
|
}
|
||||||
|
global_logs = []
|
||||||
|
|
||||||
|
# --- 工具函数 ---
|
||||||
|
def log(msg: str):
|
||||||
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
full_msg = f"[{timestamp}] {msg}"
|
||||||
|
global_logs.append(full_msg)
|
||||||
|
print(full_msg)
|
||||||
|
|
||||||
|
def mask(s: str) -> str:
|
||||||
|
if not s or len(s) < 7:
|
||||||
|
return s
|
||||||
|
return f"{s[:3]}****{s[-4:]}"
|
||||||
|
|
||||||
|
def ts() -> str:
|
||||||
|
return datetime.now().strftime('%Y%m%d%H%M%S')
|
||||||
|
|
||||||
|
def rd_str(length: int) -> str:
|
||||||
|
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
|
||||||
|
|
||||||
|
def encode(s: str) -> str:
|
||||||
|
return ''.join(chr(ord(c) + 2) for c in s)
|
||||||
|
|
||||||
|
# --- SSL与HTTP会话 ---
|
||||||
|
class CustomSSLAdapter(HTTPAdapter):
|
||||||
|
def init_poolmanager(self, *args, **kwargs):
|
||||||
|
ctx = create_urllib3_context(ciphers='DEFAULT@SECLEVEL=1:!aNULL:!eNULL:!MD5')
|
||||||
|
ctx.check_hostname = False
|
||||||
|
kwargs['ssl_context'] = ctx
|
||||||
|
return super().init_poolmanager(*args, **kwargs)
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
session.verify = certifi.where()
|
||||||
|
session.headers.update({
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Linux; U; Android 12; zh-cn) AppleWebKit/533.1 (KHTML, like Gecko) Version/5.0 Mobile Safari/533.1'
|
||||||
|
})
|
||||||
|
session.mount('https://', CustomSSLAdapter())
|
||||||
|
|
||||||
|
# --- 加密逻辑 ---
|
||||||
|
def encrypt_des3(data, mode='enc'):
|
||||||
|
cipher = DES3.new(KEYS['des3'], DES3.MODE_CBC, 8 * b'\0')
|
||||||
|
if mode == 'enc':
|
||||||
|
return cipher.encrypt(pad(data.encode(), 8)).hex()
|
||||||
|
return unpad(cipher.decrypt(bytes.fromhex(data)), 8).decode()
|
||||||
|
|
||||||
|
def encrypt_aes(data, key=KEYS['aes_def'], b64=False):
|
||||||
|
data = json.dumps(data, separators=(',', ':')) if isinstance(data, (dict, list)) else data
|
||||||
|
cipher = AES.new(key if isinstance(key, bytes) else key.encode(), AES.MODE_ECB)
|
||||||
|
enc = cipher.encrypt(pad(data.encode(), 16))
|
||||||
|
return base64.b64encode(enc).decode() if b64 else enc.hex()
|
||||||
|
|
||||||
|
def encrypt_rsa(data, key_type='data', out='hex'):
|
||||||
|
cipher = PKCS1_v1_5.new(RSA.import_key(KEYS[f'{key_type}_rsa']))
|
||||||
|
data = json.dumps(data, separators=(',', ':')) if isinstance(data, (dict, list)) else data
|
||||||
|
if out == 'hex':
|
||||||
|
return ''.join(cipher.encrypt(data[i:i+32].encode()).hex() for i in range(0, len(data), 32))
|
||||||
|
return base64.b64encode(cipher.encrypt(data.encode())).decode()
|
||||||
|
|
||||||
|
# --- 请求函数:完全屏蔽请求/响应网络日志,仅捕获异常打印 ---
|
||||||
|
def api_req(url: str, method: str = 'POST', raw: bool = False, **kwargs) -> Union[Dict[str, Any], str]:
|
||||||
|
try:
|
||||||
|
r = session.request(method, url, timeout=15, **kwargs)
|
||||||
|
if raw:
|
||||||
|
return r.text
|
||||||
|
return r.json()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[网络异常] {str(e)}")
|
||||||
|
return '' if raw else {}
|
||||||
|
|
||||||
|
# --- 唯一登录方法:login---
|
||||||
|
def login_v2(phone: str, password: str, android_id: str):
|
||||||
|
"""登录"""
|
||||||
|
m_phone = mask(phone)
|
||||||
|
log(f"[登录] {m_phone} 开始登录")
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"headerInfos": {
|
||||||
|
"code": "userLoginNormal",
|
||||||
|
"timestamp": ts(),
|
||||||
|
"broadAccount": "",
|
||||||
|
"broadToken": "",
|
||||||
|
"clientType": "#11.0.0#channel8#Xiaomi 20#",
|
||||||
|
"shopId": "20002",
|
||||||
|
"source": "110003",
|
||||||
|
"sourcePassword": "Sid98s",
|
||||||
|
"token": "",
|
||||||
|
"userLoginName": encode(phone)
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"attach": "test",
|
||||||
|
"fieldData": {
|
||||||
|
"loginType": "4",
|
||||||
|
"accountType": "",
|
||||||
|
"loginAuthCipherAsymmertric": encrypt_rsa(
|
||||||
|
f"Xiaomi 20 8.0.0.{android_id[:12]}{phone}{ts()}{password}0$$$0.",
|
||||||
|
'login', 'b64'
|
||||||
|
),
|
||||||
|
"deviceUid": "",
|
||||||
|
"phoneNum": encode(phone),
|
||||||
|
"isChinatelecom": "",
|
||||||
|
"systemVersion": "8.0.0",
|
||||||
|
"androidId": encode(android_id),
|
||||||
|
"loginAuthCipher": "",
|
||||||
|
"authentication": encode(password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = api_req(
|
||||||
|
'https://appgologin.189.cn:9031/login/client/userLoginNormal',
|
||||||
|
json=body
|
||||||
|
)
|
||||||
|
if not isinstance(res, dict):
|
||||||
|
log(f"失败] {m_phone} 响应非JSON")
|
||||||
|
return None
|
||||||
|
|
||||||
|
login_data = res.get('responseData', {}).get('data', {}).get('loginSuccessResult')
|
||||||
|
if not login_data:
|
||||||
|
err_msg = res.get('responseData', {}).get('data', {}).get('resultMsg') or '接口返回无登录数据'
|
||||||
|
log(f"[登录失败] {m_phone}: {err_msg}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取Ticket
|
||||||
|
xml = f'''<Request>
|
||||||
|
<HeaderInfos>
|
||||||
|
<Code>getSingle</Code>
|
||||||
|
<Timestamp>{ts()}</Timestamp>
|
||||||
|
<BroadAccount></BroadAccount>
|
||||||
|
<BroadToken></BroadToken>
|
||||||
|
<ClientType>#9.6.1#channel50#iPhone 14 Pro Max#</ClientType>
|
||||||
|
<ShopId>20002</ShopId>
|
||||||
|
<Source>110003</Source>
|
||||||
|
<SourcePassword>Sid98s</SourcePassword>
|
||||||
|
<Token>{login_data["token"]}</Token>
|
||||||
|
<UserLoginName>{phone}</UserLoginName>
|
||||||
|
</HeaderInfos>
|
||||||
|
<Content>
|
||||||
|
<Attach>test</Attach>
|
||||||
|
<FieldData>
|
||||||
|
<TargetId>{encrypt_des3(login_data["userId"])}</TargetId>
|
||||||
|
<Url>4a6862274835b451</Url>
|
||||||
|
</FieldData>
|
||||||
|
</Content>
|
||||||
|
</Request>'''
|
||||||
|
xml_res = api_req(
|
||||||
|
'https://appgologin.189.cn:9031/map/clientXML',
|
||||||
|
data=xml,
|
||||||
|
headers={'Content-Type': 'application/xml'},
|
||||||
|
raw=True
|
||||||
|
)
|
||||||
|
if not isinstance(xml_res, str):
|
||||||
|
log(f"[获取Ticket失败] {m_phone} 返回非字符串")
|
||||||
|
return None
|
||||||
|
if '过期' in xml_res or '校验错误' in xml_res:
|
||||||
|
log(f"[获取Ticket失败] {m_phone} 票据校验异常")
|
||||||
|
return None
|
||||||
|
if '<Ticket>' not in xml_res:
|
||||||
|
log(f"[Ticket异常] {m_phone} 响应缺失Ticket")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
ticket = xml_res.split('<Ticket>')[1].split('</Ticket>')[0]
|
||||||
|
uid = encrypt_des3(ticket, 'dec')
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[解析Ticket失败] {m_phone}: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 统一登录获取Bearer
|
||||||
|
auth_body = encrypt_aes(
|
||||||
|
{"ticket": uid, "backUrl": "https%3A%2F%2Fwapact.189.cn%3A9001", "platformCode": "P201010301", "loginType": 2},
|
||||||
|
KEYS['aes_login'],
|
||||||
|
True
|
||||||
|
)
|
||||||
|
auth_res = api_req(
|
||||||
|
'https://wapact.189.cn:9001/unified/user/login',
|
||||||
|
data=auth_body,
|
||||||
|
headers={'Content-Type': 'application/json'}
|
||||||
|
)
|
||||||
|
user_info = {
|
||||||
|
**login_data,
|
||||||
|
'uid': uid,
|
||||||
|
'phoneNbr': phone
|
||||||
|
}
|
||||||
|
if isinstance(auth_res, dict) and auth_res.get('code') == 0:
|
||||||
|
user_info['Authorization'] = f"Bearer {auth_res['biz']['token']}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
log(f"[统一登录警告] {m_phone} 未获取Bearer,抽奖功能不可用")
|
||||||
|
|
||||||
|
return user_info
|
||||||
|
|
||||||
|
# --- 任务执行(签到、抽奖等)---
|
||||||
|
def sign_tasks(user: dict):
|
||||||
|
m = mask(user['phoneNbr'])
|
||||||
|
log(f"[任务开始] {m}")
|
||||||
|
|
||||||
|
sso_url = f"https://wappark.189.cn/jt-sign/ssoHomLogin?ticket={user['uid']}"
|
||||||
|
sso = api_req(sso_url, method='GET')
|
||||||
|
if not isinstance(sso, dict) or not sso or 'sign' not in sso:
|
||||||
|
log(f"[获取sign失败] {m} 中断所有签到任务")
|
||||||
|
return
|
||||||
|
sign_header = {'sign': sso['sign']}
|
||||||
|
|
||||||
|
# 签到
|
||||||
|
log(f"[签到] {m} 执行每日签到")
|
||||||
|
api_req(
|
||||||
|
'https://wappark.189.cn/jt-sign/webSign/sign',
|
||||||
|
json={"encode": encrypt_aes({"phone": user['phoneNbr'], "date": int(time.time()*1000)})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_and_award(path, key, days_list, label):
|
||||||
|
res = api_req(
|
||||||
|
f'https://wappark.189.cn/jt-sign/{path}',
|
||||||
|
json={"para": encrypt_rsa({"phone": user['phoneNbr']})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
if not isinstance(res, dict):
|
||||||
|
return
|
||||||
|
days = str(res.get('data', {}).get(key) if 'data' in res else res.get(key, 0))
|
||||||
|
log(f"[{label}] {m}: {days}天")
|
||||||
|
if days in days_list:
|
||||||
|
log(f"[{label}领奖] {m} 达标{days}天,领取奖励")
|
||||||
|
api_req(
|
||||||
|
'https://wappark.189.cn/jt-sign/webSign/exchangePrize',
|
||||||
|
json={"para": encrypt_rsa({"phone": user['phoneNbr'], "type": days})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
|
||||||
|
check_and_award('api/home/userStatusInfo', 'signDay', ['7'], '连签')
|
||||||
|
check_and_award('webSign/continueSignDays', 'continueSignDays', ['15', '28'], '累签')
|
||||||
|
|
||||||
|
# 金豆转盘
|
||||||
|
if 'Authorization' in user:
|
||||||
|
log(f"[抽奖] {m} 查询转盘活动")
|
||||||
|
tab = api_req(
|
||||||
|
f"https://wapact.189.cn:9001/gateway/golden/api/queryTurnTable?userType=1&_={int(time.time()*1000)}",
|
||||||
|
method='GET',
|
||||||
|
headers={'Authorization': user['Authorization']}
|
||||||
|
)
|
||||||
|
if isinstance(tab, dict) and tab.get('code') == 0:
|
||||||
|
act_id = tab['biz']['wzTurntable']['code']
|
||||||
|
chk = api_req(
|
||||||
|
f"https://wapact.189.cn:9001/gateway/standQuery/detail/check?activityId={act_id}",
|
||||||
|
method='GET',
|
||||||
|
headers={'Authorization': user['Authorization']}
|
||||||
|
)
|
||||||
|
if isinstance(chk, dict) and chk.get('code') == 0:
|
||||||
|
info = chk.get('biz', {}).get('resultInfo', {})
|
||||||
|
remain = info.get('userMaximum', 0) - info.get('userCount', 0)
|
||||||
|
log(f"[抽奖] {m} 剩余可抽奖次数:{remain}次")
|
||||||
|
for idx in range(remain):
|
||||||
|
log(f"[抽奖] {m} 进行第{idx+1}次抽奖")
|
||||||
|
api_req(
|
||||||
|
'https://wapact.189.cn:9001/gateway/golden/api/lottery',
|
||||||
|
json={"activityId": act_id},
|
||||||
|
headers={'Authorization': user['Authorization']}
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
log(f"[抽奖] {m} 查询剩余次数接口异常")
|
||||||
|
else:
|
||||||
|
log(f"[抽奖] {m} 无可用转盘活动")
|
||||||
|
else:
|
||||||
|
log(f"[抽奖] {m} 缺少Bearer凭证,跳过抽奖")
|
||||||
|
|
||||||
|
# 任务列表
|
||||||
|
tasks_res = api_req(
|
||||||
|
'https://wappark.189.cn/jt-sign/webSign/homepage',
|
||||||
|
json={"para": encrypt_rsa({"phone": user['phoneNbr'], "shopId": "20001", "type": "hg_qd_zrwzjd"})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
if isinstance(tasks_res, dict):
|
||||||
|
tasks = tasks_res.get('data', {}).get('biz', {}).get('adItems', [])
|
||||||
|
log(f"[任务列表] {m} 待完成任务总数:{len(tasks)}个")
|
||||||
|
for t in tasks:
|
||||||
|
if t.get('taskState') in ['0', '1'] and t.get('contentOne') == '18':
|
||||||
|
log(f"[任务执行] {m} 执行任务:{t.get('title', '未知任务')}")
|
||||||
|
api_req(
|
||||||
|
'https://wappark.189.cn/jt-sign/webSign/polymerize',
|
||||||
|
json={"para": encrypt_rsa({"phone": user['phoneNbr'], "jobId": t['taskId']})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# 喂食
|
||||||
|
log(f"[喂食] {m} 开始宠物喂食")
|
||||||
|
for i in range(10):
|
||||||
|
res = api_req(
|
||||||
|
'https://wappark.189.cn/jt-sign/paradise/food',
|
||||||
|
json={"para": encrypt_rsa({"phone": user['phoneNbr']})},
|
||||||
|
headers=sign_header
|
||||||
|
)
|
||||||
|
msg = res.get('resoultMsg', '') if isinstance(res, dict) else ''
|
||||||
|
if "最大" in msg or "已达" in msg or not msg:
|
||||||
|
if msg:
|
||||||
|
log(f"[喂食结束] {m} {msg}")
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
log(f"[任务全部完成] {m}")
|
||||||
|
|
||||||
|
# --- 主程序 ---
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raw = os.environ.get('dxlin', '')
|
||||||
|
if not raw:
|
||||||
|
log("未找到环境变量 dxlin,请按格式设置:手机号#密码#AndroidID(AndroidID从小程序“脚本置顶”中获取),多账号换行分隔")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
accs = [line.strip().split('#') for line in raw.strip().split('\n') if line.strip() and '#' in line]
|
||||||
|
if not accs:
|
||||||
|
log("未解析到有效账号,请检查格式(手机号#密码#AndroidID)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for idx, parts in enumerate(accs, 1):
|
||||||
|
phone = parts[0].strip()
|
||||||
|
pwd = parts[1].strip() if len(parts) > 1 else ''
|
||||||
|
android_id = parts[2].strip() if len(parts) > 2 else ''
|
||||||
|
|
||||||
|
# 检查AndroidID是否提供
|
||||||
|
if not android_id:
|
||||||
|
log(f"[账号{idx}] 错误:缺少AndroidID,格式应为 手机号#密码#AndroidID,AndroidID请从小程序“脚本置顶”中获取。跳过该账号。")
|
||||||
|
continue
|
||||||
|
|
||||||
|
log(f"\n{'='*10} 账号[{idx}] {mask(phone)} {'='*10}")
|
||||||
|
user = login_v2(phone, pwd, android_id)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
sign_tasks(user)
|
||||||
|
else:
|
||||||
|
log(f"[账号跳过] {mask(phone)} 登录失败,不执行任务")
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# --- 推送所有日志 ---
|
||||||
|
if HAS_NOTIFY and global_logs:
|
||||||
|
try:
|
||||||
|
full_log = "\n".join(global_logs)
|
||||||
|
notify.send('电信任务推送', full_log)
|
||||||
|
log("通知推送成功")
|
||||||
|
except Exception as e:
|
||||||
|
log(f"通知推送失败: {str(e)}")
|
||||||
|
else:
|
||||||
|
log("未启用通知模块或无运行日志")
|
||||||
Reference in New Issue
Block a user