分享一个py并发多账号抢雪王的源码
感谢大家帮忙解密,故现做出回馈!
配置区的token必须修改,自行抓包,不做叙述
代码包含两部分:mxbc.py 和cdn_sign.js
以下是mxbc.py 代码:
import time
import hashlib
import requests
import execjs
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
# -------------------- 配置 --------------------
USE_PROXY = True # 是否使用代理
MS_DELAY = 100 # 每条请求间隔(毫秒)
#http://api.xiequ.cn/VAD/GetIp.aspx?act=get&uid=145115&vkey=D43DF3444A308F29BB1594E44743D39E&num=200&time=30&plat=1&re=1&type=0&so=1&ow=1&spl=1&addr=&db=1
#http://api.xiequ.cn/VAD/GetIp.aspx?act=get&uid=145115&vkey=D43DF3444A308F29BB1594E44743D39E&num=200&time=30&plat=1&re=1&type=2&so=1&ow=1&spl=1&addr=&db=1
PROXY_API_URL = "http://route.xiongmaodaili.com/xiongmao-web/selfControl/getIP?secret=a90af7283ce2b06282474b749703d0f7&code=1&orderNo=ZK20250519150454K0dmYctE&type=0&returnAccount=1" # IP 代理URL
# ✅ 完整 TOKEN 列表(手机号 , token )
TOKEN_PAIRS = [
( "账号备注1" , "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ3eF8xNDoxNzQ3MzE4NTQxfQ.WYZUZOGrI_suFQjoPA0HW6Bz9fvhLtobwr4BIDDOEjUNBmPc-6JR5jshY_hNKryFhbGXKg" ),
( " 账号备注2", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ3eG1pbmlfMTkyMzAyNsImlhdCI6MTMyMDA0Mn0.GjNBiwxP8ZQokN7FkoNjtr9Am-4coTZAzFskKcnHT7lob1PA0Q2yjBHKPqoYs3pef8qxL-5qvMZAeemyuBnPuw" ),
( " 账号备注3", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwaG9uZV8xODE4MTUwMIiiaWF0IjoxNzQ3NTU4MzI2fQ.K6PJJKARaBEvOzz_JewicoVKW6iZ-9GK3qMi175bNbgBrqrWpDIJVamNMKGGgi8DoozcQ3EDgusDXp4vr9J-4w" ),
( " 账号备注4", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwaG9uZV8xNDQ5MDoxNzQ3NTU4NTg1fQ.dYl9pT5TQN8iFho_Cbvk5VbPKKmnkpM2gKzylK90maZ_7cmbHmsw098wJ498nGcu5TE3SgZPdvawO6L0KwLulQ" ),
( " 账号备注15", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ3eF8xNzkyOTI1NzA4NjI5Mzc3MDI2Ii9h7gUy9Yu8I8GIy8E9wnwPZ14o6syPNQTTd17LdfGXl0bcHduznnsB0yCqDAu21n2VPH_w" )
]
JS_FILE = "cdn_sign.js" # JS 文件路径
SECRET_WORD = " 爆汁蓝莓一口沦陷! " #口令!!!!!每天必须改!!!!!!!!!!!
# ROUND_TIME = "17:00" # 这部分已经写了区当前时间的整点,注释了,如需开启删除 submit_task函数里的ROUND_TIME 即可。SUBMIT_TIMES = 30 # 最大任务数
MAX_WORKERS = 10 # 提高并发数
FIXED_SALT = "c274bac6493544b89d9c4f9d8d542b84" #固定,不许修改
# -------------------- 加载 JS --------------------
with open(JS_FILE, "r" , encoding= "utf-8" ) as f:
js_code = f.read()
ctx = execjs.compile(js_code)
# -------------------- 工具函数 --------------------
def generate_sign(data: dict) -> str:
raw = (
f"marketingId= { data[ 'marketingId' } "
f"&round= { data[ 'round' } "
f"&s= { data[ 's' } "
f"&secretword= { data[ 'secretword' } "
f"&stamp= { data[ 'stamp' } "
)
return hashlib.md5((raw + FIXED_SALT).encode( "utf-8" )).hexdigest()
def fetch_proxies() -> list:
if not USE_PROXY:
return [ " 直连 " ]
try :
resp = requests.get(PROXY_API_URL, timeout= 10 )
if resp.status_code == 200 :
proxies = resp.text.strip().splitlines()
valid_ips = [ip for ip in proxies if ":" in ip and len(ip.split( ":" )) == 2 ]
if valid_ips:
print( f" 获取到 { len(valid_ips) } 个有效代理 IP" )
return valid_ips
else :
print( " 代理内容格式异常,将使用直连。 " )
else :
print( f" 代理请求失败,状态码: { resp.status_code } ,将使用直连。 " )
except Exception as e:
print( f" 代理接口异常: { e } ,将使用直连。 " )
return [ " 直连 " ]
def submit_task(phone: str, token: str, proxy_ip: str) -> bool:
ROUND_TIME = datetime.now().strftime( '%H:00' )
base_url = "https://activity.mxbc.net/skc/api/v1/secretword/confirm"
payload = {
"marketingId" : "1923016284317048833" ,
"round" : ROUND_TIME,
"secretword" : SECRET_WORD,
"s" : 2 ,
"stamp" : int(time.time() * 1000 )
}
payload[ "sign" ] = generate_sign(payload)
headers = {
"Content-Type" : "application/json" ,
"Access-Token" : token,
"User-Agent" : "Mozilla/5.0" ,
"version" : "2.5.3"
}
try :
final_url = ctx.call( "get_cdn_seaech" , base_url, payload)
except Exception as e:
print( f"[ { proxy_ip } ] URL 生成失败: { e } " )
return False
proxies = { "http" : f"http:// { proxy_ip } " , "https" : f"http:// { proxy_ip } " } if proxy_ip != " 直连 " else None
try :
resp = requests.post(final_url, headers=headers, json=payload, proxies=proxies, timeout= 5 )
if resp.status_code == 200 :
print( f"[ { proxy_ip } ] [ { phone } ] 成功 : { resp.json() } " )
return True
else :
print( f"[ { proxy_ip } ] [ { phone } ] 失败 : 状态码 { resp.status_code } " )
return False
except Exception as e:
print( f"[ { proxy_ip } ] [ { phone } ] 请求异常: { e } " )
return False
def main():
proxies = fetch_proxies()
tasks = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
for i in range(SUBMIT_TIMES):
phone, token = TOKEN_PAIRS[i % len(TOKEN_PAIRS)]
proxy_ip = proxies[i % len(proxies)]
tasks.append(executor.submit(submit_task, phone, token, proxy_ip))
for future in as_completed(tasks):
_ = future.result()
# -------------------- 程序入口 --------------------
if __name__ == "__main__" :
print( f" { datetime.now().strftime( '%Y-%m-%d %H:%M:%S' ) } - 开始执行任务 " )
main()
这部分代码通过借鉴人杰大佬的方法进行修改,可直接使用。
cdn_sign.js:
function Compress (data, level) {
const mapChars = "DGi0YA7BemWnQjCl4+bR3f8SKIF9tUz/xhr2oEOgPpac=61ZqwTudLkM5vHyNXsVJ" ;
if (data === null ) return "" ;
let dictionary = {},
newSequence = {},
currentSequence = "" ,
dictSize = 2 ,
bitsUsed = 3 ,
codeWidth = 2 ,
output = [],
bitBuffer = 0 ,
bitCount = 0 ;
const pushBits = (charCode, length) => {
for (let j = 0 ; j < length; j ++) {
bitBuffer = (bitBuffer << 1 ) | (1 & charCode);
if (bitCount === level - 1 ) {
output .push (mapChars .charAt (bitBuffer ));
bitBuffer = 0 ;
bitCount = 0 ;
} else {
bitCount ++;
}
charCode >>= 1 ;
}
};
for (let i = 0 ; i < data.length ; i ++) {
const currentChar = data.charAt (i );
if (!dictionary .hasOwnProperty (currentChar )) {
dictionary [currentChar ] = bitsUsed ++;
newSequence [currentChar ] = true ;
}
const combined = currentSequence + currentChar ;
if (dictionary .hasOwnProperty (combined )) {
currentSequence = combined ;
} else {
if (newSequence .hasOwnProperty (currentSequence )) {
if (currentSequence .charCodeAt (0 ) < 256 ) {
pushBits (0 , codeWidth );
pushBits (currentSequence .charCodeAt (0 ), 8 );
} else {
pushBits (1 , codeWidth );
pushBits (currentSequence .charCodeAt (0 ), 16 );
}
dictSize --;
if (dictSize === 0 ) {
dictSize = 1 << codeWidth ++;
}
delete newSequence [currentSequence ];
} else {
pushBits (dictionary [currentSequence ], codeWidth );
}
dictSize --;
if (dictSize === 0 ) {
dictSize = 1 << codeWidth ++;
}
dictionary [combined ] = bitsUsed ++;
currentSequence = currentChar ;
}
}
if (currentSequence !== "" ) {
if (newSequence .hasOwnProperty (currentSequence )) {
if (currentSequence .charCodeAt (0 ) < 256 ) {
pushBits (0 , codeWidth );
pushBits (currentSequence .charCodeAt (0 ), 8 );
} else {
pushBits (1 , codeWidth );
pushBits (currentSequence .charCodeAt (0 ), 16 );
}
dictSize --;
if (dictSize === 0 ) {
dictSize = 1 << codeWidth ++;
}
delete newSequence [currentSequence ];
} else {
pushBits (dictionary [currentSequence ], codeWidth );
}
dictSize --;
if (dictSize === 0 ) {
dictSize = 1 << codeWidth ++;
}
}
pushBits (2 , codeWidth );
while (true ) {
bitBuffer <<= 1 ;
if (bitCount === level - 1 ) {
output .push (mapChars .charAt (bitBuffer ));
break ;
}
bitCount ++;
}
return output .join ("" );
}
function get_sig (data) {
data = encodeURIComponent (data);
let chars = 0 ;
for (let i = 0 ; i < data.length ; i ++) {
chars = (chars << 7 ) - chars + 398 + data.charCodeAt (i );
chars |= 0 ;
}
return chars ;
}
function get_ua (data, noPad) {
if (null == data) return "" ;
let res = Compress (data, 6 );
if (noPad) return res ;
switch (res .length % 4 ) {
case 0 : return res ;
case 1 : return res + "===" ;
case 2 : return res + "==" ;
case 3 : return res + "=" ;
}
}
function parseUrl (url) {
const urlPattern = /^(https?: \/\/ )?([^ \/ ?#]+)([^?#]*)(\?[^#]*)?(#.*)?$/ ;
const matches = url.match (urlPattern );
if (!matches ) throw new Error ('Invalid URL' );
const searchParams = {};
if (matches [4 ]) {
matches [4 ].substring (1 ).split ('&' ).forEach (param => {
const [key , value ] = param.split ('=' );
searchParams [key ] = value ;
});
}
if (matches [1 ]) {
matches [1 ] = matches [1 ].replace ('//' , '' );
}
return {
protocol : matches [1 ] || '' ,
host : matches [2 ] || '' ,
pathname : matches [3 ] || '' ,
search : matches [4 ] || '' ,
searchParams : searchParams ,
hash : matches [5 ] || '' ,
toString : function () {
return ` ${this .protocol }// ${this .host }${this .pathname }${this .search }${this .hash }` ;
}
};
}
function get_cdn_seaech (url, Data) {
let Obj = parseUrl (url);
let search = '' ;
if (Obj .search ) {
let queryData = Obj .search .replace ("?" , "" ).split ("&" );
search = queryData .filter (v => !v.startsWith ("refer__1972" )).join ("&" ) || "" ;
if (Obj .search !== "" ) Obj .search = "?" + search ;
}
let signText = Obj .toString ();
if (Data) signText += JSON .stringify (Data);
signText = get_sig (signText ) + "|0|" + new Date ().getTime () + "|1" ;
let keyName = (function () {
const keyNames = ["type__" , "refer__" , "ipcity__" , "md5__" , "decode__" , "encode__" , "time__" , "timestamp__" , "type__" ];
let codes = 0 ;
for (let i = 0 ; i < Obj .host .length ; i ++) {
codes += Obj .host .charCodeAt (i );
}
return keyNames [codes % keyNames .length ] + (codes % 10000 );
})();
let ua = get_ua (signText , true );
if (!Obj .search ) {
Obj .search = `? ${keyName }=` + encodeURIComponent (ua );
} else {
Obj .search += `& ${keyName }=` + encodeURIComponent (ua );
}
return Obj .toString ();
}
[size=13.0667px]
[size=13.0667px]
[size=13.0667px]