From a4b10b13a826106be92e8b6b18564c92ebe64a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=87=95=E9=B9=8F?= Date: Mon, 5 Dec 2022 17:46:05 +0800 Subject: [PATCH] init --- JDMain.py | 70 ++++++++ __init__.py | 0 api_timer.py | 52 ++++++ app.py | 75 +++++++++ cookie-yibeizi.txt | 1 + jdseckillAPIv2.py | 350 ++++++++++++++++++++++++++++++++++++++++ myapp.py | 81 ++++++++++ requirements.txt | 3 + success.txt | 30 ++++ tools/ModifiedBase64.py | 112 +++++++++++++ tools/__init__.py | 0 tools/jd_sign.py | 51 ++++++ tools/mylogger.py | 31 ++++ tools/utils.py | 163 +++++++++++++++++++ 14 files changed, 1019 insertions(+) create mode 100644 JDMain.py create mode 100644 __init__.py create mode 100644 api_timer.py create mode 100644 app.py create mode 100644 cookie-yibeizi.txt create mode 100644 jdseckillAPIv2.py create mode 100644 myapp.py create mode 100644 requirements.txt create mode 100644 success.txt create mode 100644 tools/ModifiedBase64.py create mode 100644 tools/__init__.py create mode 100644 tools/jd_sign.py create mode 100644 tools/mylogger.py create mode 100644 tools/utils.py diff --git a/JDMain.py b/JDMain.py new file mode 100644 index 0000000..709fa11 --- /dev/null +++ b/JDMain.py @@ -0,0 +1,70 @@ +import warnings + +import time +from datetime import datetime + +import requests.utils + +from jdseckillAPIv2 import JDSecKillAPI +from tools.mylogger import logger + +warnings.filterwarnings('ignore') +from concurrent.futures import ThreadPoolExecutor, wait + + +class JDSecKillSubmit(JDSecKillAPI): + + def __init__(self, sku, ck): + super().__init__(sku=sku, ck=ck) + self.sku = sku + self.ck = ck + + def log(self, str_p): + logger.info(str_p) + + def doKill(self, sk, order_data): + resp_json = self.submit_order(order_data=order_data, sk=sk) + if resp_json is not None: + txt = order_data['address']['name'] + "抢购结果:" + str(resp_json) + self.log(txt) + if resp_json['success']: + self.send_message(order_data['address']['name'] + '抢购成功, 订单:' + str(resp_json)) + + def appoint_task(self): + try: + resp_json = self.appoint_sku() + print("预约结果:%s" % str(resp_json)) + self.log('appoint:' + str(resp_json)) + except Exception as e: + print(str(e)) + + def killSku(self): + + try: + token_params = self.get_token_key() + print(datetime.now()) + divide_url = self.get_appjmp(token_params=token_params) + print(datetime.now()) + captcha_url = self.get_divide(divide_url=divide_url) + print(datetime.now()) + seckill_url = self.get_captcha(captcha_url=captcha_url) + print(datetime.now()) + + # todo 测试url + # seckill_url = 'https://marathon.jd.com/seckillM/seckill.action?skuId=' + self.sku + '&num=1&rid=1639830212' + resp = self.visit_seckill(seckill_url=seckill_url) + + order_data = self.init_action() + print(datetime.now()) + if order_data is not None: + for k in range(50): + sk_val = self.get_tak() + print("计算sk:%s" % str(sk_val)) + self.doKill(sk_val, order_data) + time.sleep(0.5) + return True + + return False + except Exception as e: + print(str(e)) + return False diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api_timer.py b/api_timer.py new file mode 100644 index 0000000..fd18d12 --- /dev/null +++ b/api_timer.py @@ -0,0 +1,52 @@ +import json +import time + +import requests + + +class JDTimer: + + def __init__(self): + self.headers = { + 'user-agent': 'okhttp/3.12.1;jdmall;android;version/10.5.0;build/95837;', + 'content-type': 'application/x-www-form-urlencoded', + } + self.session = requests.Session() + try: + self.jd_time() + except Exception as e: + print(e) + + def jd_time(self): + """ + 从京东服务器获取时间毫秒 + :return: + """ + url = 'https://api.m.jd.com/client.action?functionId=queryMaterialProducts&client=wh5' + ret = self.session.get(url, headers=self.headers, allow_redirects=False, verify=False).text + js = json.loads(ret) + + return int(js["currentTime2"]) + + def local_time(self): + """ + 获取本地毫秒时间 + :return: + """ + return int(round(time.time() * 1000)) + + def local_jd_time_diff(self): + """ + 计算本地与京东服务器时间差 + :return: + """ + try: + return self.local_time() - self.jd_time() + except Exception as e: + print(e) + return 0 + +if __name__ == '__main__': + jdtimer = JDTimer() + for i in range(5): + print(jdtimer.local_jd_time_diff()) diff --git a/app.py b/app.py new file mode 100644 index 0000000..b33835f --- /dev/null +++ b/app.py @@ -0,0 +1,75 @@ +import time +import sys +import os + + +curPath = os.path.abspath(os.path.dirname(__file__)) +rootPath = os.path.split(curPath)[0] +sys.path.append(rootPath) + +from api_timer import JDTimer +from tools import utils +from JDMain import JDSecKillSubmit +from tools.mylogger import logger + + +class JD: + + def __init__(self): + sku = '100012043978' + + def log(self, str_p): + logger.info(str_p) + + def yuyueSku(self, sku, ck): + self.jdapi = JDSecKillSubmit(sku, ck) + self.jdapi.appoint_task() + + def killSku(self): + for i in range(5): + print('第%d次kill---------------------------->' % i) + result = self.jdapi.killSku() + if result: + break + + def syncTime(self): + jdTimer = JDTimer() + return jdTimer.local_jd_time_diff() + + +if __name__ == '__main__': + + jd = JD() + jd.log("正在读取cookie") + file = open("cookie-xqs.txt") + ck = file.read() + ck = 'pin=jd_694172ad51580;wskey=AAJgfXWSAEBLNQvLpLSYBYcBCqSXPF0LSFJksN9TUW-zjHxTQzDHotSD_LUE5EqkKLhBT3r0b3jCYYm33xg21oLFXWdad7mG;whwswswws=ypBVaXEwFjiJxh/kEB/6O2f6fohXOoCM3D8YNlQLR9tc=;unionwsws={"devicefinger":"eidA852c81205asb09A7r2HGTjCoC1RNl7MXytPQPScu2F6yVo+5IRMfDrQg8oI21UoEA0ZlhW+EgFWItSqxk76gpd5LLB389Tk1jXNw+GmMO1H7tOt5","jmafinger":"ypBVaXEwFjiJxh\/kEB\/6O2f6fohXOoCM3D8YNlQLR9tc="};' + jd.log(ck) + + sku = '100012043978' + killTime = '2022-09-24 11:59:59.900' + killTimeTs = utils.getTimeStamp(killTime, format='%Y-%m-%d %H:%M:%S.%f') + + syncedTime = False + hasYuyue = False + + timeDiff = 0 # 时差 + + while True: + nowTimeTs = int(time.time() * 1000) + killDiff = killTimeTs - nowTimeTs + print("时间剩余%s秒" % str(int(killDiff / 1000))) + + if killDiff < 5 * 60 * 1000 and not syncedTime: + syncedTime = True + timeDiff = jd.syncTime() + print("时差:%s" % str(timeDiff)) + killTimeTs = killTimeTs + timeDiff + elif killDiff < 1 * 60 * 1000 and not hasYuyue: + hasYuyue = True + jd.yuyueSku(sku=sku, ck=ck) + elif killDiff < 0: + print("时差:%s" % str(timeDiff)) + jd.killSku() + break + time.sleep(0.01) diff --git a/cookie-yibeizi.txt b/cookie-yibeizi.txt new file mode 100644 index 0000000..54d7276 --- /dev/null +++ b/cookie-yibeizi.txt @@ -0,0 +1 @@ +pin=jd_41d74c83f8224;wskey=AAJjKGIcAED51MS4xl3rMhkuKoz3aIdtHgYbbY_gONAtDcvoXgp0nCjPWKcjgXoBw3ZNcm-HVECeryK5NBFQiErqyGbAcqt-;whwswswws=JD012145b9WBZ52FxcoJ166359098310502j1UGQOp_xw1sHnZf2TBLxIAVcI1pr_ptSG7GbI4Jh8JHPzpgXIVd6vHgcp4KUP7bIcL92ot4SobMNdIjsb6CeO0xXfdGg1DB166dh3n~lV6zvij0xVW8O87RwFvqyq8bfocRq2uG0yIAc6GJfPzG3Vt7W-bSkl68yzw3cggRUZpEKUJ0VpkW0x0Q0aGrCFLasnWujHANJMp2D1tUHzbtF-NKQkCBUiGE1ZKdTwYkeVoNQaOoU87ivURTKramyPzaIW67BRI6kPNkr3PLHsHc;unionwsws={"devicefinger":"eidAfa90812247s5vgf0CkxeRgeWBZfWowyK9r4H9Y2h1wPWRDa\/+LyYAJea3ooTv4ePNmFFv1ZArvKR74Nk1rlZWoaTpHUgZCeqmC5Lix6Hz1eGUEN6","jmafinger":"JD012145b9WBZ52FxcoJ166359098310502j1UGQOp_xw1sHnZf2TBLxIAVcI1pr_ptSG7GbI4Jh8JHPzpgXIVd6vHgcp4KUP7bIcL92ot4SobMNdIjsb6CeO0xXfdGg1DB166dh3n~lV6zvij0xVW8O87RwFvqyq8bfocRq2uG0yIAc6GJfPzG3Vt7W-bSkl68yzw3cggRUZpEKUJ0VpkW0x0Q0aGrCFLasnWujHANJMp2D1tUHzbtF-NKQkCBUiGE1ZKdTwYkeVoNQaOoU87ivURTKramyPzaIW67BRI6kPNkr3PLHsHc"}; \ No newline at end of file diff --git a/jdseckillAPIv2.py b/jdseckillAPIv2.py new file mode 100644 index 0000000..c303d23 --- /dev/null +++ b/jdseckillAPIv2.py @@ -0,0 +1,350 @@ +import json +import warnings + +import requests +import re +from urllib import parse +import time + +from wxpusher import WxPusher + +from tools import utils +from tools.jd_sign import getSign + +warnings.filterwarnings('ignore') + + +def getUrlParams(url): + res = dict(parse.parse_qsl(url)) + return res + + +def get_cookie_string(cookie): + cookie_string = '' + for cookie_key in cookie.keys(): + cookie_string += '%s=%s;' % (cookie_key, cookie[cookie_key]) + return cookie_string + + +def get_jd_time(): + response = requests.get(url='https://api.m.jd.com/client.action?functionId=queryMaterialProducts&client=wh5') + print(response.json()) + + +def get_sk(data): + data_val = [val for val in data['data'].values()] + n, o, p, q, r, s = data_val[0], data_val[1], data_val[2], data_val[3], data_val[4], data_val[5] + sk_val = '' + if n == 'cca': + sk_val = p[14:19].lower() + o[5:15].upper() + if n == 'ab': # check ok + sk_val = r[10:18] + s[2:13].lower() + if n == 'ch': + sk_val = q.upper() + r[6:10].upper() + if n == 'cbc': # check ok + sk_val = q[3:13].upper() + p[10:19].lower() + if n == 'by': + sk_val = o[5:8] + re.sub('a', 'c', p, flags=re.IGNORECASE) + if n == 'xa': + sk_val = o[1:16] + s[4:10] + if n == 'cza': + sk_val = q[6:19].lower() + s[5:11] + if n == 'cb': + sk_val = s[5:14] + p[2:13].upper() + + return sk_val + + +class JDSecKillAPI: + def __init__(self, sku, ck): + self.skuId = sku + self.s = requests.session() + self.sku = sku + self.ck = ck + self.aid = '' + self.eid = 'eidAccfa8121das3mOM5swaGRcSw7E22kO50H5jjOzSUZLdxjWFZLi3ATsvj875K/RWM0W4ztxrbes6TNeio5uhWCeJIKObRbFo6NaYRyMqYuMJ2MM5D' + self.uuid = '351792042184702-6dd3f46ae26a' + self.uts = '0f31TVRjBSsqndu4/jgUPz6uymy50MQJAUhDDPnNIByQiRdjk5SK6CXmdShdxGaPRUd2+o8JNufp2fsgzyZpKhao+6lXsd3TXF8+jFmN/08eWFOjD36AXn3DPYPoc8MKL9ccf40wpTgO1wSP1oliin/fcMzzPHSxzINfo/svdCXHzPsMY3eZLWP0KGqPLr5fXA1RZRoBs4xl/IYv7E80OQ==' + self.wifiBssid = '' + self.ua = 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36' + + def appoint_sku(self): + headers = { + 'user-agent': 'okhttp/3.12.1;jdmall;android;version/10.5.0;build/95837;', + 'content-type': 'application/x-www-form-urlencoded', + 'cookie': self.ck, + 'jdc-backup': self.ck, + } + + ts = int(time.time() * 1000) + uuid = self.uuid + ep = utils.get_ep(ts, uuid) + + query_params = { + 'functionId': 'appoint', + 'clientVersion': '10.5.0', + 'build': '95837', + 'client': 'android', + 'd_brand': 'HUAWEI', + 'd_model': 'LIO-AN00', + 'osVersion': '7.1.2', + 'screen': '1920*1080', + 'partner': 'hhqj02', + 'aid': self.aid, + 'eid': self.eid, + 'sdkVersion': '29', + 'lang': 'zh_CN', + 'harmonyOs': '0', + 'uuid': self.uuid, + 'area': '', + 'networkType': 'wifi', + 'wifiBssid': self.wifiBssid, + 'uts': self.uts, + 'uemps': '0-0', + 'ext': '{"prstate":"0","pvcStu":"1"}', + 'ef': '1', + 'ep': json.dumps(ep, ensure_ascii=False, separators=(',', ':')), + } + reserve_url = 'https://api.m.jd.com/client.action' + + body = {"autoAddCart": "0", "bsid": "", "check": "0", "ctext": "", "isShowCode": "0", "mad": "0", + "skuId": self.skuId, "type": "1"} + + plainTextDic = { + "st": ts, # 毫秒级时间戳 + "sv": "120", + "functionId": query_params['functionId'], + "uuid": uuid, + "client": query_params['client'], + "clientVersion": query_params['clientVersion'], + "body": json.dumps(body, ensure_ascii=False, separators=(',', ':')) + } + st, sign, sv = getSign(plainTextDic) + + query_params.update(st=st) + query_params.update(sign=sign) + query_params.update(sv=sv) + + data = {'body': json.dumps(body, ensure_ascii=False, separators=(',', ':'))} + + response = self.s.post(url=reserve_url, + params=query_params, + data=data, + headers=headers, + allow_redirects=False, + verify=False, + timeout=3) + return response.json() + + def get_token_key(self): + headers = { + 'user-agent': 'okhttp/3.12.1;jdmall;android;version/10.5.0;build/95837;', + 'content-type': 'application/x-www-form-urlencoded', + 'cookie': self.ck, + 'jdc-backup': self.ck, + } + + ts = int(time.time() * 1000) + uuid = self.uuid + ep = utils.get_ep(ts, uuid) + + query_params = { + 'functionId': 'genToken', + 'clientVersion': '10.5.0', + 'build': '95837', + 'client': 'android', + 'd_brand': 'HUAWEI', + 'd_model': 'LIO-AN00', + 'osVersion': '7.1.2', + 'screen': '1920*1080', + 'partner': 'hhqj02', + 'aid': self.aid, + 'eid': self.eid, + 'sdkVersion': '29', + 'lang': 'zh_CN', + 'harmonyOs': '0', + 'uuid': self.uuid, + 'area': '', + 'networkType': 'wifi', + 'wifiBssid': self.wifiBssid, + 'uts': self.uts, + 'uemps': '0-0', + 'ext': '{"prstate":"0","pvcStu":"1"}', + 'ef': '1', + 'ep': json.dumps(ep, ensure_ascii=False, separators=(',', ':')), + } + + body = {"action": "to", "to": "https://divide.jd.com/user_routing?skuId="+self.sku} + + plainTextDic = { + "st": ts, # 毫秒级时间戳 + "sv": "120", + "functionId": query_params['functionId'], + "uuid": uuid, + "client": query_params['client'], + "clientVersion": query_params['clientVersion'], + "body": json.dumps(body, ensure_ascii=False, separators=(',', ':')) + } + st, sign, sv = getSign(plainTextDic) + + query_params.update(st=st) + query_params.update(sign=sign) + query_params.update(sv=sv) + + data = {'body': json.dumps(body, ensure_ascii=False, separators=(',', ':'))} + + response = self.s.post(url='https://api.m.jd.com/client.action', + params=query_params, + data=data, + headers=headers, + allow_redirects=False, + verify=False, + timeout=3) + # token_key = response.json()['tokenKey'] + # print('Token Key: ----------> %s' % response.json()) + # print(response.status_code) + json_obj = response.json() + print('Get genToken--------------->%s' % str(json_obj)) + return json_obj + + def get_appjmp(self, token_params): + headers = { + 'user-agent': self.ua + } + appjmp_url = token_params['url'] + params = { + 'to': 'https://divide.jd.com/user_routing?skuId=%s' % self.skuId, + 'tokenKey': token_params['tokenKey'] + } + + response = self.s.get(url=appjmp_url, params=params, allow_redirects=False, verify=False, headers=headers) + print('Get Appjmp跳转链接-------------->%s' % response.headers['Location']) + return response.headers['Location'] + + def get_divide(self, divide_url): + headers = { + 'user-agent': self.ua + } + response = self.s.get(url=divide_url, allow_redirects=False, verify=False, headers=headers) + print('Get Divide跳转链接-------------->%s' % response.headers['Location']) + return response.headers['Location'] + + def get_captcha(self, captcha_url): + headers = { + 'user-agent': self.ua + } + response = self.s.get(url=captcha_url, allow_redirects=False, verify=False, headers=headers) + print('Get Captcha跳转链接-------------->%s' % response.headers['Location']) + return response.headers['Location'] + + def visit_seckill(self, seckill_url): + headers = { + 'user-agent': self.ua + } + response = self.s.get(url=seckill_url, allow_redirects=False, verify=False, headers=headers) + return response + + def init_action(self, num=1): + + try: + headers = { + 'user-agent': self.ua, + 'Connection': 'keep-alive' + } + init_action_url = 'https://marathon.jd.com/seckillnew/orderService/init.action' + data = { + 'sku': self.skuId, + 'num': num, + 'id': 0, + 'provinceId': 0, + 'cityId': 0, + 'countyId': 0, + 'townId': 0, + } + response = self.s.post(url=init_action_url, data=data, allow_redirects=False, verify=False, headers=headers) + print('init action返回数据:%s' % response.text) + return response.json() + except Exception as e: + print(str(e)) + return None + + + def get_tak(self): + try: + headers = { + 'user-agent': self.ua, + 'Connection': 'keep-alive' + } + tak_url = 'https://tak.jd.com/t/871A9?_t=%d' % (int(round(time.time() * 1000))) + response = self.s.get(url=tak_url, allow_redirects=False, verify=False, headers=headers) + sk_val = get_sk(data=response.json()) + return sk_val + except Exception as e: + print(str(e)) + return '' + + def submit_order(self, order_data, sk): + try: + headers = { + 'user-agent': self.ua, + 'Connection': 'keep-alive' + } + submit_order_url = 'https://marathon.jd.com/seckillnew/orderService/submitOrder.action?skuId=%s' % self.skuId + address_info = order_data['address'] + invoice_info = order_data['invoiceInfo'] + data = { + 'num': order_data['seckillSkuVO']['num'], + 'addressId': address_info['id'], + 'yuShou': True, + 'isModifyAddress': False, + 'name': address_info['name'], + 'provinceId': address_info['provinceId'], + 'provinceName': address_info['provinceName'], + 'cityId': address_info['cityId'], + 'cityName': address_info['cityName'], + 'countyId': address_info['countyId'], + 'countyName': address_info['countyName'], + 'townId': address_info['townId'], + 'townName': address_info['townName'], + 'addressDetail': address_info['addressDetail'], + 'mobile': address_info['mobile'], + 'mobileKey': address_info['mobileKey'], + 'email': '', + 'invoiceTitle': invoice_info['invoiceTitle'], + 'invoiceContent': invoice_info['invoiceContentType'], + 'invoicePhone': invoice_info['invoicePhone'], + 'invoicePhoneKey': invoice_info['invoicePhoneKey'], + 'invoice': True, + 'codTimeType': '3', + 'paymentType': '4', + 'overseas': '0', + 'token': order_data['token'], + 'sk': sk, + } + + response = self.s.post(url=submit_order_url, data=data, allow_redirects=False, verify=False, headers=headers) + return response.json() + except Exception as e: + print('submit error--->') + return None + + def send_message(self, content): + try: + # 推送token + PUSH_TOKEN = 'AT_4XxUFvSjSLWTlFhX1nFmIepe1RNoGq8b' + + UIDS = [ + 'UID_D77yyDO0pT7K0f1q2UijDTGnGthF', + ] + msg = WxPusher.send_message(content, + uids=UIDS, + token=PUSH_TOKEN) + except Exception as e: + print('send_message error--->'+str(e)) + +if __name__ == '__main__': + ck = 'pin=jd_694172ad51580;wskey=AAJgfXWSAEBLNQvLpLSYBYcBCqSXPF0LSFJksN9TUW-zjHxTQzDHotSD_LUE5EqkKLhBT3r0b3jCYYm33xg21oLFXWdad7mG;whwswswws=ypBVaXEwFjiJxh/kEB/6O2f6fohXOoCM3D8YNlQLR9tc=;unionwsws={"devicefinger":"eidA852c81205asb09A7r2HGTjCoC1RNl7MXytPQPScu2F6yVo+5IRMfDrQg8oI21UoEA0ZlhW+EgFWItSqxk76gpd5LLB389Tk1jXNw+GmMO1H7tOt5","jmafinger":"ypBVaXEwFjiJxh\/kEB\/6O2f6fohXOoCM3D8YNlQLR9tc="};' + ck = 'pin=jd_4d36cfba7dab3;wskey=AAJjGfg2AEDbVEKTmVkgEvWx-McNVFq980fU5IHeej0nLaHVNdOtzlTLYBDCEucWGJ4G9teOfEzntfRGd6KQyjXgY3rjDDn1;whwswswws=JD012145b9v1fE5xpOyD1662646298263061cDoZzexYxAp8KLqT9smx6eCUAMi_vgpfvlNGugBkHqboOLvPxFIIgdchS83Af4aCPkH9MZR8ybX3QlO52r5tGF45M9TrieypOzwu3NAoOs0a40rty~w8wI98OE8okFESgoJ3sI8KD1kO1fXEYyQfW7eDyRLBI1VlbD72BRZvaIwvqX8mX245w7Jl7eI9mR2U_2IrtbtCm-NOZyDZr7LWiepMRuTaaO-iRDLr0M_4xyeRgstrh1ombJE27yWpzAQs4PLrFSWhVWGnPf597V0y6FJhG3YaXA;unionwsws={"devicefinger":"eidAea1681212dsbM9nriXryQHq\/kLPLj0nYnTPZ7QyYY2ofy15o7Ohj1DMrIY\/71NnUV3ow\/8dwlD\/hgR5tfjQJNif\/1NbGABbd9A29YETZGl0TuF45","jmafinger":"JD012145b9v1fE5xpOyD1662646298263061cDoZzexYxAp8KLqT9smx6eCUAMi_vgpfvlNGugBkHqboOLvPxFIIgdchS83Af4aCPkH9MZR8ybX3QlO52r5tGF45M9TrieypOzwu3NAoOs0a40rty~w8wI98OE8okFESgoJ3sI8KD1kO1fXEYyQfW7eDyRLBI1VlbD72BRZvaIwvqX8mX245w7Jl7eI9mR2U_2IrtbtCm-NOZyDZr7LWiepMRuTaaO-iRDLr0M_4xyeRgstrh1ombJE27yWpzAQs4PLrFSWhVWGnPf597V0y6FJhG3YaXA"};' + jdapi = JDSecKillAPI('100012043978', ck) + print('预约结果--->', jdapi.appoint_sku()) + print('gentoken结果--->', jdapi.get_token_key('100012043978')) diff --git a/myapp.py b/myapp.py new file mode 100644 index 0000000..fda9136 --- /dev/null +++ b/myapp.py @@ -0,0 +1,81 @@ +import time +import sys +import os +curPath = os.path.abspath(os.path.dirname(__file__)) +rootPath = os.path.split(curPath)[0] +sys.path.append(rootPath) + +from multiprocessing import Process +import requests + +from tools import utils +from JDMain import JDSecKillSubmit +from api_timer import JDTimer + + + +def syncTime(): + jdTimer = JDTimer() + return jdTimer.local_jd_time_diff() + + +def yuyueSku(sku, ck): + jdapi = JDSecKillSubmit(sku, ck) + jdapi.appoint_task() + + +def killSku(sku, ck): + jdapi = JDSecKillSubmit(sku, ck) + for i in range(5): + print('第%d次kill---------------------------->' % i) + if jdapi.killSku(): + break + +def work(killTime, ck): + print('ck:', ck) + sku = '100012043978' + killTimeTs = utils.getTimeStamp(killTime, format='%Y-%m-%d %H:%M:%S.%f') + + syncedTime = False + hasYuyue = False + + timeDiff = 0 # 时差 + + while True: + nowTimeTs = int(time.time() * 1000) + killDiff = killTimeTs - nowTimeTs + print("时间剩余%s秒" % str(int(killDiff / 1000))) + + if killDiff < 5 * 60 * 1000 and not syncedTime: + syncedTime = True + timeDiff = syncTime() + print("时差:%s" % str(timeDiff)) + killTimeTs = killTimeTs + timeDiff + elif killDiff < 2 * 60 * 1000 and not hasYuyue: + hasYuyue = True + yuyueSku(sku=sku, ck=ck) + elif killDiff < 0: + print("时差:%s" % str(timeDiff)) + killSku(sku=sku, ck=ck) + break + time.sleep(0.01) + + +if __name__ == '__main__': + + # 抢购时间 + killTime = '2022-11-09 11:59:59.800' + + # cookie + cks = [ + 'pin=jd_41d74c83f8224;wskey=AAJjKGIcAED51MS4xl3rMhkuKoz3aIdtHgYbbY_gONAtDcvoXgp0nCjPWKcjgXoBw3ZNcm-HVECeryK5NBFQiErqyGbAcqt-;whwswswws=JD012145b9WBZ52FxcoJ166359098310502j1UGQOp_xw1sHnZf2TBLxIAVcI1pr_ptSG7GbI4Jh8JHPzpgXIVd6vHgcp4KUP7bIcL92ot4SobMNdIjsb6CeO0xXfdGg1DB166dh3n~lV6zvij0xVW8O87RwFvqyq8bfocRq2uG0yIAc6GJfPzG3Vt7W-bSkl68yzw3cggRUZpEKUJ0VpkW0x0Q0aGrCFLasnWujHANJMp2D1tUHzbtF-NKQkCBUiGE1ZKdTwYkeVoNQaOoU87ivURTKramyPzaIW67BRI6kPNkr3PLHsHc;unionwsws={"devicefinger":"eidAfa90812247s5vgf0CkxeRgeWBZfWowyK9r4H9Y2h1wPWRDa\/+LyYAJea3ooTv4ePNmFFv1ZArvKR74Nk1rlZWoaTpHUgZCeqmC5Lix6Hz1eGUEN6","jmafinger":"JD012145b9WBZ52FxcoJ166359098310502j1UGQOp_xw1sHnZf2TBLxIAVcI1pr_ptSG7GbI4Jh8JHPzpgXIVd6vHgcp4KUP7bIcL92ot4SobMNdIjsb6CeO0xXfdGg1DB166dh3n~lV6zvij0xVW8O87RwFvqyq8bfocRq2uG0yIAc6GJfPzG3Vt7W-bSkl68yzw3cggRUZpEKUJ0VpkW0x0Q0aGrCFLasnWujHANJMp2D1tUHzbtF-NKQkCBUiGE1ZKdTwYkeVoNQaOoU87ivURTKramyPzaIW67BRI6kPNkr3PLHsHc"};', + ] + + p = [] + for i in range(len(cks)): + p1 = Process(target=work, args=(killTime, cks[i],)) + p1.start() + p.append(p1) + + for i in range(len(cks)): + p[i].join() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..81b3957 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +requests==2.27.1 +wxpusher==2.2.0 +urllib3==1.26.9 \ No newline at end of file diff --git a/success.txt b/success.txt new file mode 100644 index 0000000..19e1231 --- /dev/null +++ b/success.txt @@ -0,0 +1,30 @@ +计算sk:zr7oflx9p335iWYurie +Expecting value: line 1 column 1 (char 0) +Get Divide跳转链接-------------->https://marathon.jd.com/m/captcha.html?sid=b8385a800e93b184ff8bb16a0e79d8aw&skuId=10001 +2043978&mid=rvXgpdduG3wnlgfPzI2wXkvpgNqF4IA9qcWds4ffipY +Get Captcha跳转链接-------------->https://marathon.jd.com/mobile/koFail.html +init action返回数据:{"address":{"addressDetail":"西李村","areaCode":"999999","cityId":2900,"cityName":"济宁市","countyI +d":2917,"countyName":"金乡县","defaultAddress":false,"id":6188735630,"mobile":"133****4689","mobileKey":"f7dcf949de1a7b4 +27463a9dd90f632e1","name":"王秋娟","overseas":0,"phone":"","postCode":"","provinceId":13,"provinceName":"山东","selected +":true,"townId":25860,"townName":"鱼山街道","yuyueAddress":false},"buyNum":2,"code":"200","invoiceInfo":{"invoiceContent +Type":1,"invoicePhone":"133****4689","invoicePhoneKey":"f7dcf949de1a7b427463a9dd90f632e1","invoiceTitle":4,"invoiceType" +:3},"jingdouBO":{"canUseCount":0,"forbidUseJingdou":false,"jingdouDiscount":0,"jingdouSteps":[],"pointsDeductionRate":50 +,"rate":100,"totalCount":179,"usedCount":0},"orderPriceBO":{"couponAbleNum":0,"couponDiscount":0.00,"couponDiscountTotal +":0.00,"couponSelectNum":0,"couponTypeList":[],"freight":0.00,"giftCardAbleNum":0,"giftCardDiscount":0.00,"giftCardSelec +tNum":0,"jingdouDiscount":0,"manjianDiscount":0.00,"orderTax":0.00,"productTotalPrice":1499.00,"redPacketDeductionAmount +":0,"redPacketIsSelected":false,"redPacketTotalAmount":0,"showXuZhongInfo":false,"totalPrice":1499.00,"weight":"1.12","y +unfeiDiscount":0.00},"payment":{"paymentId":4,"paymentName":"在线支付"},"preSaleBO":{"hidePrice":false,"refundDeposit":f +alse},"seckillOrderExt":{},"seckillSkuVO":{"color":"飞天 53%vol 500ml 贵州茅台酒","extMap":{"YuShou":"1","is7ToReturn":" +0","new7ToReturn":"8","thwa":"0","SoldOversea":"0"},"height":0.0,"jdPrice":1499.00,"length":0.0,"num":1,"rePrice":0.00," +size":"","skuId":100012043978,"skuImgUrl":"jfs/t1/204850/31/20522/123067/61af3d74E747493f0/385d705a66b5263b.jpg","skuNam +e":"飞天 53%vol 500ml 贵州茅台酒(带杯)","skuPrice":1499.00,"thirdCategoryId":0.0,"venderName":"京东自营","venderType" +:0,"weight":1.120,"width":0.0},"shipment":{"bundleUuid":"BundleRelation_1125395520593502208","obtainAllCombinationBundle +":"CombinationBundleRelation_1125395520425730051","obtainOrder":"-1719262869","shipmentTimeType":4,"shipmentTimeTypeName +":"标准达","shipmentType":65,"shipmentTypeName":"京东配送","uuid":"101612_1125395520610279424"},"token":"fdc90dde774b3c5 +84b613a8b33050a95","userInfoBO":{"userScore":5525}} +计算sk:6ktun1kxw1gypV4GMMQ +抢购结果:{'appUrl': 'https://mpay.m.jd.com/mpay.1ec8f70280318a51e2bb.html?appId=jd_m_msha&payId=3420cb4f0d7a4424b53f8ae7 +81f542f6', 'orderId': 250387144183, 'pcUrl': '//sko.jd.com/success/success.action?orderId=250387144183&rid=0.88556282118 +81714', 'resultCode': 0, 'skuId': 0, 'success': True, 'totalMoney': '1499.00'} +耗时:941 +Get Divide跳转链接-------------->https://marathon.jd.com/m/captcha.html?sid=b8385a800e93b184ff8bb16a0e79d8aw&skuId=10001 \ No newline at end of file diff --git a/tools/ModifiedBase64.py b/tools/ModifiedBase64.py new file mode 100644 index 0000000..aba3c03 --- /dev/null +++ b/tools/ModifiedBase64.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +class ModifiedBase64(object): + + def __init__(self): + self.aae = ['K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/'] + self.aaf = [None] * 128 + + # renamed from: pf + # def m23208pf(self): + i = 0 + i2 = 0 + while True: + bArr = self.aaf + if i2 <= len(bArr) - 1: + bArr[i2] = -1 + i2 += 1 + else: + break + while True: + cArr = self.aae + if i <= len(cArr) - 1: + self.aaf[ord(cArr[i])] = int(i) + i += 1 + else: + return + + def m23207r(self, str_): + result = '' + bArr = bytearray(str_.encode('utf-8')) + + i = 0 + while i <= len(bArr) - 1: + bArr2 = [None] * 4 + b = 0 + i2 = 0 + while i2 <= 2: + i3 = i + i2 + if i3 <= len(bArr) - 1: + bArr2[i2] = (b | ((bArr[i3] & 255) >> ((i2 * 2) + 2))) + b = ((((bArr[i3] & 255) << (((2 - i2) * 2) + 2)) & 255) >> 2) + else: + bArr2[i2] = b + b = 0x40 # SignedBytes.MAX_POWER_OF_TWO; + i2 += 1 + bArr2[3] = b + + i4 = 0 + while i4 <= 3: + if bArr2[i4] <= 63: + result = result + self.aae[bArr2[i4]] + else: + result = result + '=' + i4 += 1 + i += 3 + + return result + + # renamed from: eC + def m23209eC(self, str_): + """ generated source for method m23209eC """ + bys = [] + # bytes = str_.encode('utf-8') + bytes = bytearray(str_.encode('utf-8')) + # print(bytes) + bArr = [None] * len(bytes) + i = 0 + while i <= len(bytes) - 1: + bArr[i] = self.aaf[bytes[i]] + i += 1 + i2 = 0 + while i2 <= len(bArr) - 1: + bArr2 = [None] * 3 + i3 = 0 + i4 = 0 + i7 = 0 + while i4 <= 2: + i5 = i2 + i4 + i6 = i5 + 1 + if i6 <= len(bArr) and bArr[i6] >= 0: + bArr2[i4] = ((bArr[i5] & 255) << ((i4 * 2) + 2)) | ((bArr[i6] & 255) >> (((2 - (i4 + 1)) * 2) + 2)) + i3 += 1 + i4 += 1 + while i7 <= i3 - 1: + bys.append(self.py2ja(bArr2[i7])) + i7 += 1 + i2 += 4 + return bytearray(bys).decode('utf-8') + + def py2ja(self, arr: int): + """ + python字节数组转java字节数组 + :return: + """ + while arr >= 256: + arr = arr - 256 + return arr + + +if __name__ == '__main__': + # UUID = "ENYnDJO5CNGnCJC5DzcyBWHtEJK4ENcyDJu3EG==" + v = ModifiedBase64() + # a = v.m23209eC(UUID) + # print(a) + + text = '5ZIG5Ya26AQX6YQJ5YsY6SML5bsS' + print(v.m23209eC(text)) + + text = '唐冶街道刘老师' + print(v.m23207r(text)) diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/jd_sign.py b/tools/jd_sign.py new file mode 100644 index 0000000..e2af3b9 --- /dev/null +++ b/tools/jd_sign.py @@ -0,0 +1,51 @@ +import base64 +import hashlib + + +def getSign(params): + # sv传120 + key = [55, 146, 68, 104, 165, 61, 204, 127, 187, 15, 217, 136, 238, 154, 233, 90] + arg0 = [56, 48, 51, 48, 54, 102, 52, 51, 55, 48, 98, 51, 57, 102, 100, 53, 54, 51, 48, 97, 100, 48, 53, 50, 57, 102, + 55, 55, 97, 100, 98, 54] + + sign = "functionId=" + params.get("functionId") + "&body=" + params.get("body") + "&uuid=" + params.get("uuid") \ + + "&client=" + params.get("client") + "&clientVersion=" + params.get("clientVersion") + "&st=" + str( + params.get("st")) \ + + "&sv=" + str(params.get("sv")) + arg2 = bytearray(sign, 'utf-8') + # print([x for x in arg2]) + ss = [None] * len(arg2) + for i in range(len(arg2)): + R0 = arg2[i] + R2 = key[i & 15] + R4 = arg0[i & 7] + R0 = ((R2 ^ R0) ^ R4) + R2 + R2 = (R2 ^ R0) + R1 = arg0[i & 7] + R2 = (R2 ^ R1) + # print(R2) + ss[i] = R2 + m = hashlib.md5() + m.update(base64.b64encode(bytes(i % 256 for i in ss))) + return str(params.get("st")), m.hexdigest(), str(params.get("sv")) + # return "st=" + str(params.get("st")) + "&sign=" + m.hexdigest() + "&sv=" + str(params.get("sv")) + + +def generateSign(): + params = { + "st": 1648992407201, # 毫秒级时间戳 + "sv": "120", + "functionId": 'serverConfig', + "uuid": '947934e94e0cb9e4', + "client": "android", + "clientVersion": '10.4.6', + # "body": '{"to":"https://plogin.m.jd.com/jd-mlogin/static/html/appjmp_blank.html"}' + "body": '{}' + } + # return getSign(params) + print(getSign(params)) + # print("正确结果:st=1648992407201&sign=474c50af52257126ae65700b825bdf4c&sv=120") + + +if __name__ == '__main__': + generateSign() diff --git a/tools/mylogger.py b/tools/mylogger.py new file mode 100644 index 0000000..cd53bc1 --- /dev/null +++ b/tools/mylogger.py @@ -0,0 +1,31 @@ +import logging +import logging.handlers +import os + +''' +os.getcwd()代表py执行目录 +日志模块 +''' + +# 创建log文件夹 +new_path = os.path.join(os.getcwd(), 'log') +if not os.path.exists(new_path): + os.makedirs(new_path) + +LOG_FILENAME = 'log/jd.log' +logger = logging.getLogger() + +def set_logger(): + if not logger.handlers: + logger.setLevel(logging.INFO) + formatter = logging.Formatter('%(asctime)s - %(levelname)s: %(message)s') + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + file_handler = logging.handlers.RotatingFileHandler( + LOG_FILENAME, maxBytes=5*1024*1024, backupCount=5, encoding="utf-8") + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + +set_logger() diff --git a/tools/utils.py b/tools/utils.py new file mode 100644 index 0000000..bb23e60 --- /dev/null +++ b/tools/utils.py @@ -0,0 +1,163 @@ +import hashlib +import platform +import random +import socket +from datetime import datetime +import sys +import os + +from http.cookies import SimpleCookie + +import json +import re + +from tools.ModifiedBase64 import ModifiedBase64 + + +def text_to_json(text, regex): + pattern = re.compile(regex) + text_list = re.findall(pattern, text) + _json = json.loads(text_list[0]) + return _json + + +def remove_unused_symbol(text): + return text.replace('\n', '').replace('\r', '').replace('\t', '') + + +def remove_redundant_comma(text): + """ + 移除json多余逗号,避免json.loads报错 + """ + rex = r"""(?<=[}\]"'])\s*,\s*(?!\s*[{["'])""" + return re.sub(rex, "", text, 0) + + +def resource_path(relative_path): + if getattr(sys, 'frozen', False): + base_path = sys._MEIPASS + else: + base_path = os.path.abspath("../..") + return os.path.join(base_path, relative_path) + + +def export_file_name(): + return "ck-export-" + datetime.now().strftime('%Y%m%d%H%M%S') + ".txt" + + +def desktop_path(): + if platform.system() == 'Windows': + return r"C:\Users\Administrator\Desktop" + else: + return "/Users/xiaoqingsong/Desktop" + + +def is_json(text): + if text is None: + return False + try: + json.loads(text) + except ValueError: + return False + return True + + +def getTimeStamp(time): + """ + :param time: 形如 2021-01-20 10:10:20.120 + :return: + """ + ts = datetime.timestamp(datetime.strptime(time, '%Y-%m-%d %H:%M:%S.%f')) + return int(ts * 1000) + + +def getTimeStamp(time, format): + """ + :param format: %Y-%m-%d %H:%M:%S + :param time: 形如 2021-01-20 10:10:20 + :return: + """ + ts = datetime.timestamp(datetime.strptime(time, format)) + return int(ts * 1000) + + +def getNowTime(): + return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-7] + + +def getCookieFromStr(rawStr): + cookie = SimpleCookie() + cookie.load(rawStr) + # Even though SimpleCookie is dictionary-like, it internally uses a Morsel object + # which is incompatible with requests. Manually construct a dictionary instead. + cookies = {} + for key, morsel in cookie.items(): + cookies[key] = morsel.value + return cookies + + +def get_random_number_str(length): + """ + 生成随机数字字符串 + :param length: 字符串长度 + :return: + """ + num_str = ''.join(str(random.choice(range(10))) for _ in range(length)) + return num_str + + +def randomUUID(): + random_number = get_random_number_str(15) + random_str = hashlib.md5(random_number.encode('utf-8')).hexdigest()[:12] + return random_number + "-" + random_str + + +def get_host_ip(): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(('8.8.8.8', 80)) + ip = s.getsockname()[0] + finally: + s.close() + + return ip + + +def mb_encrypt(str): + v = ModifiedBase64() + return v.m23207r(str) + + +def get_ep(ts, uuid): + area = '13_1000_40488_54435' + d_model = 'PDNM00' + wifiBssid = 'unknown' + osVersion = '11' + d_brand = 'OPPO' + screen = '2161*1080' + uuid = uuid + aid = uuid + openudid = uuid + hdid = '' + + ep = { + 'hdid': mb_encrypt(hdid), + 'ts': ts, + 'ridx': -1, + 'cipher': { + 'area': mb_encrypt(area), + 'd_model': mb_encrypt(d_model), + 'wifiBssid': mb_encrypt(wifiBssid), + 'osVersion': mb_encrypt(osVersion), + 'd_brand': mb_encrypt(d_brand), + 'screen': mb_encrypt(screen), + 'uuid': mb_encrypt(uuid), + 'aid': mb_encrypt(aid), + 'openudid': mb_encrypt(openudid), + }, + 'ciphertype': 5, + 'version': '1.2.0', + 'appname': 'com.jingdong.app.mall', + } + + return ep