788 lines
32 KiB
Python
788 lines
32 KiB
Python
#!/bin/env python3
|
||
# -*- coding: utf-8 -*
|
||
'''
|
||
项目名称: JD_OpenCard
|
||
Author: Curtin
|
||
功能:JD入会开卡领取京豆
|
||
CreateDate: 2021/5/4 下午1:47
|
||
UpdateTime: 2021/5/15
|
||
'''
|
||
version = 'v1.0.5'
|
||
readmes = """
|
||
# JD入会领豆 - 轻松日撸千豆
|
||
## 目录结构
|
||
JD-Script/ #仓库
|
||
|-- LICENSE
|
||
|-- OpenCrad # 主目录
|
||
| |-- jd_OpenCrad.py # 主代码 (必要)
|
||
| |-- log # 临时目录(可删除)
|
||
| |-- OpenCardConfig.ini # 只配置文件(必要)
|
||
| |-- Readme.md # 说明
|
||
| `-- shopid.txt # shopid存放文件
|
||
`-- README.md
|
||
|
||
### `【兼容环境】`
|
||
1.Python3.3+ 环境
|
||
2.兼容ios设备软件:Pythonista 3(已测试正常跑,其他软件自行测试)
|
||
3.Windows exe
|
||
|
||
安装依赖模块 :
|
||
pip3 install requests
|
||
执行:
|
||
python jd_OpenCrad.py
|
||
## `【更新记录】`
|
||
2021.5.15:(v1.0.5)
|
||
* 新增远程获取shopid功能
|
||
- isRemoteSid=yes #开启
|
||
* 修改已知Bug
|
||
|
||
2021.5.9:(v1.0.4 Beta)
|
||
* 优化代码逻辑
|
||
* 打包exe版本测试
|
||
|
||
2021.5.8:(v1.0.3)
|
||
* 优化记忆功能逻辑:
|
||
- cookiek个数检测
|
||
- shopid个数检测
|
||
- 上一次中断最后记录的用户id检测不存在本次ck里面
|
||
- 临时文件log/memory.json是否存在
|
||
- 以上任意一条命中则记忆接力功能不生效。
|
||
|
||
2021.5.7:(v1.0.2)
|
||
* 优化代码逻辑
|
||
* 修复已知Bug
|
||
|
||
2021.5.5:(v1.0.1)
|
||
* 新增记忆功能,如中断后下次跑会接着力跑(默认开启)
|
||
- memory= True
|
||
* 新增仅记录shopid,不入会功能(默认关闭)
|
||
- onlyRecord = no
|
||
* 修复已知Bug
|
||
|
||
2021.5.4:(v1.0.0)
|
||
* 支持多账号
|
||
- JD_COOKIE=pt_key=xxx;pt_pin=xxx;&pt_key=xxx;pt_pin=xxx; #多账号&分隔
|
||
* 限制京豆数量入会,例如只入50豆以上
|
||
- openCardBean = 50
|
||
* 双线程运行
|
||
- 默认开启,且您没得选择。
|
||
* 记录满足条件的shopid 【record= True】默认开启 (./log 目录可删除)
|
||
- log/可销卡汇总.txt #记录开卡送豆的店铺销卡链接
|
||
- log/shopid-yyyy-mm-dd.txt #记录当天所有入会送豆的shopid
|
||
- log/可销卡用户xxx.txt #记录用户可销卡的店铺
|
||
|
||
### `【用户参数配置说明】`
|
||
### 主配置文件[ OpenCardConfig.ini ] 请保持utf-8默认格式
|
||
|
||
变量 | 值 | 说明
|
||
---- | ----- | ------
|
||
JD_COOKIE | pt_key=xxx;pt_pin=xxx; | 必要(多账号&分隔)
|
||
openCardBean | 30 | int,入会送豆满足此值,否则不入会
|
||
record | yes或no | 布尔值,是否记录符合条件的shopid(默认yes)
|
||
onlyRecord | yes或no |布尔值, yes:仅记录,不入会(默认no)
|
||
memory | yes或no | 布尔值,开启记忆功能,接力上一次异常中断位置继续。(默认yes)
|
||
printlog | yes或no | 布尔值,yes:只打印部分日志 no:打印所有日志
|
||
sleepNum | yes或no | Float,限制速度,单位秒,如果请求过快报错适当调整0.5秒以上
|
||
isRemoteSid | yes或no | 布尔值,yes:使用作者远程仓库更新的id,no:使用本地shopid.txt的id
|
||
#### $\color{red}{兼容Env环境(如有配置则优先使用,适合AC、云服务环境等)}$
|
||
export JD_COOKIE='pt_key=xxx;pt_pin=xxx;' (多账号&分隔)
|
||
export openCardBean=30
|
||
export xxx=xxx
|
||
|
||
#### Ps:您可以到以下途径获取最新的shopid.txt,定期更新:
|
||
|
||
###### [GitHub仓库](https://github.com/curtinlv/JD-Script)
|
||
###### [Gitee仓库](https://gitee.com/curtinlv/JD-Script)
|
||
###### [TG频道](https://t.me/TopStyle2021)
|
||
###### 关注公众号【TopStyle】回复:shopid
|
||

|
||
#
|
||
@Last Version: %s
|
||
|
||
@Last Time: 2021-05-15
|
||
|
||
@Author: Curtin
|
||
#### **仅以学习交流为主,请勿商业用途、禁止违反国家法律 ,转载请留个名字,谢谢!**
|
||
|
||
# End.
|
||
[回到顶部](#readme)
|
||
""" % version
|
||
|
||
################################ 【Main】################################
|
||
import time, os, sys, datetime
|
||
import requests
|
||
import random, string
|
||
import re, json,base64
|
||
from urllib.parse import unquote
|
||
from threading import Thread
|
||
from configparser import RawConfigParser
|
||
|
||
# 定义一些要用到参数
|
||
requests.packages.urllib3.disable_warnings()
|
||
scriptHeader = """
|
||
════════════════════════════════════════
|
||
║ ║
|
||
║ JD入会领豆 - 轻松日撸千豆 ║
|
||
║ ║
|
||
════════════════════════════════════════
|
||
@Version: {}""".format(version)
|
||
remarks = 'Ps:您可以到以下途径获取最新的shopid.txt,定期更新:\n\n\tGitHub:https://github.com/curtinlv/JD-Script\n\n\tTG频道:https://t.me/TopStyle2021\n\n\t关注公众号【TopStyle】回复:shopid\n\n\n\t\t\t--By Curtin\n'
|
||
|
||
timestamp = int(round(time.time() * 1000))
|
||
today = datetime.datetime.now().strftime('%Y-%m-%d')
|
||
|
||
pwd = repr(os.getcwd())
|
||
pwd = pwd.replace('\'', '')
|
||
# 获取用户参数
|
||
try:
|
||
configinfo = RawConfigParser()
|
||
try:
|
||
configinfo.read(pwd + "/OpenCardConfig.ini", encoding="UTF-8")
|
||
except Exception as e:
|
||
with open(pwd + "/OpenCardConfig.ini","r",encoding="UTF-8") as config:
|
||
getConfig = config.read().encode('utf-8').decode('utf-8-sig')
|
||
with open(pwd + "/OpenCardConfig.ini","w",encoding="UTF-8") as config:
|
||
config.write(getConfig)
|
||
try:
|
||
configinfo.read(pwd + "/OpenCardConfig.ini", encoding="UTF-8")
|
||
except:
|
||
configinfo.read(pwd + "/OpenCardConfig.ini", encoding="gbk")
|
||
cookies = configinfo.get('main', 'JD_COOKIE')
|
||
openCardBean = configinfo.getint('main', 'openCardBean')
|
||
sleepNum = configinfo.getfloat('main', 'sleepNum')
|
||
record = configinfo.getboolean('main', 'record')
|
||
onlyRecord = configinfo.getboolean('main', 'onlyRecord')
|
||
memory = configinfo.getboolean('main', 'memory')
|
||
printlog = configinfo.getboolean('main', 'printlog')
|
||
isRemoteSid = configinfo.getboolean('main', 'isRemoteSid')
|
||
except Exception as e:
|
||
OpenCardConfigLabel = 1
|
||
print("参数配置有误,请检查OpenCardConfig.ini\nError:", e)
|
||
print("尝试从Env环境获取!")
|
||
|
||
# 获取系统ENV环境参数优先使用 适合Ac、云服务等环境
|
||
# JD_COOKIE=cookie (多账号&分隔)
|
||
if "JD_COOKIE" in os.environ:
|
||
cookies = os.environ["JD_COOKIE"]
|
||
# 只入送豆数量大于此值
|
||
if "openCardBean" in os.environ:
|
||
openCardBean = os.environ["openCardBean"]
|
||
# 限制速度,单位秒,如果请求过快报错适当调整0.5秒以上
|
||
if "sleepNum" in os.environ:
|
||
sleepNum = os.environ["sleepNum"]
|
||
# 是否记录符合条件的shopid,输出文件【OpenCardlog/yes_shopid.txt】 False|True
|
||
if "record" in os.environ:
|
||
record = os.environ["record"]
|
||
# 仅记录,不入会。入会有豆的shopid输出文件【OpenCardlog/all_shopid.txt】,需要record=True且onlyRecord=True才生效。
|
||
if "onlyRecord" in os.environ:
|
||
onlyRecord = os.environ["onlyRecord"]
|
||
# 开启记忆, 需要record=True且 memory= True 才生效
|
||
if "memory" in os.environ:
|
||
memory = os.environ["memory"]
|
||
# 判断参数是否存在
|
||
try:
|
||
cookies
|
||
openCardBean
|
||
record
|
||
onlyRecord
|
||
memory
|
||
printlog
|
||
isRemoteSid
|
||
except NameError as e:
|
||
var_exists = False
|
||
print("[OpenCardConfig.ini] 和 [Env环境] 都无法获取到您的参数或缺少,请配置!\nError:", e)
|
||
time.sleep(60)
|
||
exit(1)
|
||
else:
|
||
var_exists = True
|
||
|
||
# 创建临时目录
|
||
if not os.path.exists("./log"):
|
||
os.mkdir("./log")
|
||
# 记录功能json
|
||
memoryJson = {}
|
||
|
||
|
||
################################### Function ################################
|
||
|
||
def nowtime():
|
||
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
|
||
def printinfo(context, label: bool):
|
||
if label == False:
|
||
print(context)
|
||
|
||
|
||
def exitCodeFun(code):
|
||
try:
|
||
exitCode = input()
|
||
print(exitCode)
|
||
exit(code)
|
||
except:
|
||
time.sleep(3)
|
||
exit(code)
|
||
|
||
|
||
# 检测cookie格式是否正确
|
||
def iscookie():
|
||
"""
|
||
:return: cookiesList,userNameList,pinNameList
|
||
"""
|
||
cookiesList = []
|
||
userNameList = []
|
||
pinNameList = []
|
||
if 'pt_key=' in cookies and 'pt_pin=' in cookies:
|
||
r = re.compile(r"pt_key=.*?pt_pin=.*?;", re.M | re.S | re.I)
|
||
result = r.findall(cookies)
|
||
if len(result) >= 1:
|
||
print("您已配置{}个账号".format(len(result)))
|
||
for i in result:
|
||
r = re.compile(r"pt_pin=(.*?);")
|
||
pinName = r.findall(i)
|
||
pinName = unquote(pinName[0])
|
||
# 获取用户名
|
||
ck, nickname = getUserInfo(i, pinName)
|
||
if nickname != False:
|
||
cookiesList.append(ck)
|
||
userNameList.append(nickname)
|
||
pinNameList.append(pinName)
|
||
else:
|
||
continue
|
||
if len(cookiesList) > 0 and len(userNameList) > 0:
|
||
return cookiesList, userNameList, pinNameList
|
||
else:
|
||
print("没有可用Cookie,已退出")
|
||
exitCodeFun(3)
|
||
else:
|
||
print("cookie 格式错误!...本次操作已退出")
|
||
exitCodeFun(4)
|
||
else:
|
||
print("cookie 格式错误!...本次操作已退出")
|
||
exitCodeFun(4)
|
||
|
||
# 检查是否有更新版本
|
||
|
||
def gettext(url):
|
||
try:
|
||
resp = requests.get(url,timeout=60).text
|
||
if '该内容无法显示' in resp:
|
||
gettext(url)
|
||
return resp
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
def isUpdate():
|
||
url = base64.decodebytes(
|
||
b"aHR0cHM6Ly9naXRlZS5jb20vY3VydGlubHYvUHVibGljL3Jhdy9tYXN0ZXIvT3BlbkNyYWQvdXBkYXRlLmpzb24=")
|
||
try:
|
||
result = gettext(url)
|
||
result = json.loads(result)
|
||
isEnable = result['isEnable']
|
||
uPversion = result['version']
|
||
info = result['info']
|
||
readme1 = result['readme1']
|
||
readme2 = result['readme2']
|
||
readme3 = result['readme3']
|
||
if isEnable > 50 and isEnable < 150:
|
||
if version != uPversion:
|
||
print(f"\n{info},\n\n当前最新版本:【{uPversion}】\n")
|
||
print(f"{readme1}{readme2}{readme3}")
|
||
time.sleep(300)
|
||
exit(666)
|
||
else:
|
||
print(f"\n{readme1}{readme2}{readme3}")
|
||
else:
|
||
print(readme1)
|
||
print("!!! 无法使用,请联系作者。CurtinLV")
|
||
time.sleep(300)
|
||
exit(666)
|
||
|
||
except:
|
||
print("请检查您的环境是否正常!")
|
||
time.sleep(10)
|
||
exit(666)
|
||
|
||
def getUserInfo(ck, pinName):
|
||
url = 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?orgFlag=JD_PinGou_New&callSource=mainorder&channel=4&isHomewhite=0&sceneval=2&sceneval=2&callback=GetJDUserInfoUnion'
|
||
headers = {
|
||
'Cookie': ck,
|
||
'Accept': '*/*',
|
||
'Connection': 'close',
|
||
'Referer': 'https://home.m.jd.com/myJd/home.action',
|
||
'Accept-Encoding': 'gzip, deflate, br',
|
||
'Host': 'me-api.jd.com',
|
||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Mobile/15E148 Safari/604.1',
|
||
'Accept-Language': 'zh-cn'
|
||
}
|
||
try:
|
||
resp = requests.get(url=url, verify=False, headers=headers, timeout=60).text
|
||
r = re.compile(r'GetJDUserInfoUnion.*?\((.*?)\)')
|
||
result = r.findall(resp)
|
||
userInfo = json.loads(result[0])
|
||
nickname = userInfo['data']['userInfo']['baseInfo']['nickname']
|
||
return ck, nickname
|
||
except Exception:
|
||
print(f"用户【{pinName}】Cookie 已失效!请重新获取。")
|
||
return ck, False
|
||
|
||
|
||
# 设置Headers
|
||
def setHeaders(cookie, intype):
|
||
if intype == 'mall':
|
||
headers = {
|
||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||
"Host": "shop.m.jd.com",
|
||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15",
|
||
"Accept-Language": "zh-cn",
|
||
"Accept-Encoding": "gzip, deflate, br",
|
||
# "Connection": "keep-alive"
|
||
"Connection": "close"
|
||
}
|
||
return headers
|
||
elif intype == 'JDApp':
|
||
headers = {
|
||
'Cookie': cookie,
|
||
'Accept': "*/*",
|
||
'Connection': "close",
|
||
'Referer': "https://shopmember.m.jd.com/shopcard/?",
|
||
'Accept-Encoding': "gzip, deflate, br",
|
||
'Host': "api.m.jd.com",
|
||
'User-Agent': "jdapp;iPhone;9.4.8;14.3;809409cbd5bb8a0fa8fff41378c1afe91b8075ad;network/wifi;ADID/201EDE7F-5111-49E8-9F0D-CCF9677CD6FE;supportApplePay/0;hasUPPay/0;hasOCPay/0;model/iPhone13,4;addressid/;supportBestPay/0;appBuild/167629;jdSupportDarkMode/0;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1",
|
||
'Accept-Language': "zh-cn"
|
||
}
|
||
return headers
|
||
elif intype == 'mh5':
|
||
headers = {
|
||
'Cookie': cookie,
|
||
'Accept': "*/*",
|
||
'Connection': "close",
|
||
'Referer': "https://shopmember.m.jd.com/shopcard/?",
|
||
'Accept-Encoding': "gzip, deflate, br",
|
||
'Host': "api.m.jd.com",
|
||
'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
|
||
'Accept-Language': "zh-cn"
|
||
|
||
}
|
||
return headers
|
||
|
||
|
||
# 记录符合件的shopid到本地文件保存 当前目录:OpenCardlog/shopid-yyyy-mm-dd.txt 或 log-yyyy-mm-dd.txt
|
||
def outfile(filename, context, iscover):
|
||
"""
|
||
:param filename: 文件名 默认txt格式
|
||
:param context: 写入内容
|
||
:param iscover: 是否覆盖 False or True
|
||
:return:
|
||
"""
|
||
if record == True:
|
||
try:
|
||
if iscover == False:
|
||
with open(pwd + "/log/{0}".format(filename), "a+", encoding="utf-8") as f1:
|
||
f1.write("{}\n".format(context))
|
||
elif iscover == True:
|
||
with open(pwd + "/{0}".format(filename), "w+", encoding="utf-8") as f1:
|
||
f1.write("{}".format(context))
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
|
||
# 记忆功能 默认双线程
|
||
def memoryFun(startNum, threadNum, usernameLabel, username, getallbean, userCount):
|
||
global memoryJson
|
||
if memory == True:
|
||
if usernameLabel == True:
|
||
memoryJson['allShopidNum'] = endShopidNum
|
||
memoryJson['currUser{}'.format(threadNum)] = username
|
||
memoryJson['t{}_startNum'.format(threadNum)] = startNum
|
||
memoryJson['allUserCount'] = userCount
|
||
elif usernameLabel == False:
|
||
try:
|
||
memoryJson['{}'.format(username)]
|
||
memoryJson['{}'.format(username)] += getallbean
|
||
except:
|
||
memoryJson['{}'.format(username)] = getallbean
|
||
|
||
else:
|
||
pass
|
||
try:
|
||
if os.path.exists(pwd + "/log"):
|
||
with open(pwd + "/log/memory.json", "w", encoding="utf-8") as f:
|
||
json.dump(memoryJson, f, indent=4)
|
||
else:
|
||
pass
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
|
||
# 获取记忆配置
|
||
def getMemory():
|
||
"""
|
||
:return: memoryJson
|
||
"""
|
||
if os.path.exists(pwd + "/log/memory.json"):
|
||
with open(pwd + "/log/memory.json", "r", encoding="utf-8") as f:
|
||
memoryJson = json.load(f)
|
||
if len(memoryJson) > 0:
|
||
return memoryJson
|
||
else:
|
||
pass
|
||
|
||
def rmCount():
|
||
if os.path.exists(pwd + "/log/入会汇总.txt"):
|
||
os.remove(pwd + "/log/入会汇总.txt")
|
||
|
||
# 判断是否启用记忆功能
|
||
def isMemory(memorylabel, startNum1, startNum2, midNum, endNum, pinNameList):
|
||
"""
|
||
:param memorylabel: 记忆标签
|
||
:param startNum1: 线程1默认开始位置
|
||
:param startNum2: 线程2默认开始位置
|
||
:param midNum: 线程1默认结束位置
|
||
:param endNum: 线程2默认结束位置
|
||
:return: startNum1, startNum2, memorylabel
|
||
"""
|
||
if memory == True and memorylabel == 0:
|
||
try:
|
||
memoryJson = getMemory()
|
||
if memoryJson['allShopidNum'] == endNum:
|
||
currUserLabel = 0
|
||
|
||
if memoryJson['allUserCount'] == allUserCount:
|
||
for u in pinNameList:
|
||
if memoryJson['currUser1'] == u:
|
||
currUserLabel += 1
|
||
elif memoryJson['currUser2'] == u:
|
||
currUserLabel += 1
|
||
if currUserLabel > 1:
|
||
print("通知:检测到您配置的CK有变更,本次记忆功能不生效。")
|
||
rmCount()
|
||
return startNum1, startNum2, memorylabel
|
||
if memoryJson['t1_startNum'] + 1 == midNum and memoryJson['t2_startNum'] + 1 == endNum:
|
||
print(
|
||
f"\n上次已完成所有shopid,\n\nPs:您可以关注公众号或TG频道获取最新shopid。\n公众号: TopStyle\n电报TG:https://t.me/TopStyle2021\n\n请输入 0 或 1\n0 : 退出。\n1 : 重新跑一次,以防有漏")
|
||
try:
|
||
getyourNum = int(input("正在等待您的选择:"))
|
||
if getyourNum == 1:
|
||
print("Ok,那就重新跑一次~")
|
||
rmCount()
|
||
memorylabel = 1
|
||
return startNum1, startNum2, memorylabel
|
||
elif getyourNum == 0:
|
||
print("Ok,已退出~")
|
||
time.sleep(10)
|
||
exit(0)
|
||
except:
|
||
# print("Error: 您的输入有误!已退出。")
|
||
exitCodeFun(3)
|
||
else:
|
||
if memoryJson['t1_startNum']:
|
||
startNum1 = memoryJson['t1_startNum']
|
||
print(f"已启用记忆功能 memory= True,线程1从第【{startNum1}】店铺开始")
|
||
if memoryJson['t2_startNum']:
|
||
startNum2 = memoryJson['t2_startNum']
|
||
print(f"已启用记忆功能 memory= True,线程2从第【{startNum2}】店铺开始")
|
||
memorylabel = 1
|
||
return startNum1, startNum2, memorylabel
|
||
else:
|
||
print("通知:检测到您配置的CK有变更,本次记忆功能不生效。")
|
||
rmCount()
|
||
return startNum1, startNum2, memorylabel
|
||
else:
|
||
print("通知:检测到shopid有更新,本次记忆功能不生效。")
|
||
rmCount()
|
||
memorylabel = 1
|
||
return startNum1, startNum2, memorylabel
|
||
except Exception as e:
|
||
memorylabel = 1
|
||
return startNum1, startNum2, memorylabel
|
||
|
||
|
||
# 获取VenderId
|
||
def getVenderId(shopId, headers):
|
||
"""
|
||
:param shopId:
|
||
:param headers
|
||
:return: venderId
|
||
"""
|
||
url = 'https://shop.m.jd.com/?shopId={0}'.format(shopId)
|
||
resp = requests.get(url=url, verify=False, headers=headers, timeout=60)
|
||
resulttext = resp.text
|
||
r = re.compile(r'venderId: \'(\d+)\'')
|
||
venderId = r.findall(resulttext)
|
||
return venderId[0]
|
||
|
||
|
||
# 查询礼包
|
||
def getShopOpenCardInfo(venderId, headers, shopid, userName):
|
||
"""
|
||
:param venderId:
|
||
:param headers:
|
||
:return: activityId,getBean 或 返回 0:没豆 1:有豆已是会员 2:记录模式(不入会)
|
||
"""
|
||
num1 = string.digits
|
||
v_num1 = ''.join(random.sample(["1", "2", "3", "4", "5", "6", "7", "8", "9"], 1)) + ''.join(
|
||
random.sample(num1, 4)) # 随机生成一窜4位数字
|
||
url = 'https://api.m.jd.com/client.action?appid=jd_shop_member&functionId=getShopOpenCardInfo&body=%7B%22venderId%22%3A%22{2}%22%2C%22channel%22%3A406%7D&client=H5&clientVersion=9.2.0&uuid=&jsonp=jsonp_{0}_{1}'.format(
|
||
timestamp, v_num1, venderId)
|
||
resp = requests.get(url=url, verify=False, headers=headers, timeout=60)
|
||
time.sleep(sleepNum)
|
||
resulttxt = resp.text
|
||
r = re.compile(r'jsonp_.*?\((.*?)\)\;', re.M | re.S | re.I)
|
||
result = r.findall(resulttxt)
|
||
cardInfo = json.loads(result[0])
|
||
venderCardName = cardInfo['result']['shopMemberCardInfo']['venderCardName'] # 店铺名称
|
||
printinfo(f"\t╰查询入会礼包【{venderCardName}】{shopid}", printlog)
|
||
openCardStatus = cardInfo['result']['userInfo']['openCardStatus'] # 是否会员
|
||
interestsRuleList = cardInfo['result']['interestsRuleList']
|
||
if interestsRuleList == None:
|
||
printinfo("\t\t╰Oh,该店礼包已被领光了~", printlog)
|
||
return 0, 0
|
||
try:
|
||
if len(interestsRuleList) > 0:
|
||
for i in interestsRuleList:
|
||
if "京豆" in i['prizeName']:
|
||
getBean = int(i['discountString'])
|
||
activityId = i['interestsInfo']['activityId']
|
||
context = "{0}".format(shopid)
|
||
outfile(f"shopid-{today}.txt", context, False) # 记录所有送豆的shopid
|
||
in_url='https://shop.m.jd.com/?shopId={}'.format(shopid)
|
||
url = 'https://shopmember.m.jd.com/member/memberCloseAccount?venderId={}'.format(venderId)
|
||
context = "[{0}]:入会{2}豆店铺【{1}】\n\t加入会员:{4}\n\t解绑会员:{3}".format(nowtime(), venderCardName, getBean, url,in_url) # 记录
|
||
outfile("入会汇总.txt", context, False)
|
||
if getBean >= openCardBean: # 判断豆是否符合您的需求
|
||
print(f"\t╰{venderCardName}:入会赠送【{getBean}豆】,可入会")
|
||
context = "{0}".format(shopid)
|
||
outfile(f"入会{openCardBean}豆以上的shopid-{today}.txt", context, False)
|
||
if onlyRecord == True:
|
||
print("已开启仅记录,不入会。")
|
||
return 2, 2
|
||
if openCardStatus == 1:
|
||
url = 'https://shopmember.m.jd.com/member/memberCloseAccount?venderId={}'.format(venderId)
|
||
print("\t\t╰[账号:{0}]:您已经是本店会员,请注销会员卡24小时后再来~\n注销链接:{1}".format(userName, url))
|
||
context = "[{3}]:入会{1}豆,{0}销卡:{2}".format(venderCardName, getBean, url, nowtime())
|
||
outfile("可退会账号【{0}】.txt".format(userName), context, False)
|
||
return 1, 1
|
||
return activityId, getBean
|
||
else:
|
||
print(f'\t\t╰{venderCardName}:入会送【{getBean}】豆少于【{openCardBean}豆】,不入...')
|
||
if onlyRecord == True:
|
||
print("已开启仅记录,不入会。")
|
||
return 2, 2
|
||
return 0, openCardStatus
|
||
|
||
else:
|
||
pass
|
||
printinfo("\t\t╰Oh~ 该店入会京豆已被领光了", printlog)
|
||
return 0, 0
|
||
else:
|
||
return 0, 0
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
|
||
# 开卡
|
||
def bindWithVender(venderId, shopId, activityId, channel, headers):
|
||
"""
|
||
:param venderId:
|
||
:param shopId:
|
||
:param activityId:
|
||
:param channel:
|
||
:param headers:
|
||
:return: result : 开卡结果
|
||
"""
|
||
num = string.ascii_letters + string.digits
|
||
v_name = ''.join(random.sample(num, 10))
|
||
num1 = string.digits
|
||
v_num1 = ''.join(random.sample(["1", "2", "3", "4", "5", "6", "7", "8", "9"], 1)) + ''.join(random.sample(num1, 4))
|
||
qq_num = ''.join(random.sample(["1", "2", "3", "4", "5", "6", "7", "8", "9"], 1)) + ''.join(
|
||
random.sample(num1, 8)) + "@qq.com"
|
||
url = 'https://api.m.jd.com/client.action?appid=jd_shop_member&functionId=bindWithVender&body=%7B%22venderId%22%3A%22{4}%22%2C%22shopId%22%3A%22{7}%22%2C%22bindByVerifyCodeFlag%22%3A1%2C%22registerExtend%22%3A%7B%22v_sex%22%3A%22%E6%9C%AA%E7%9F%A5%22%2C%22v_name%22%3A%22{0}%22%2C%22v_birthday%22%3A%221990-03-18%22%2C%22v_email%22%3A%22{6}%22%7D%2C%22writeChildFlag%22%3A0%2C%22activityId%22%3A{5}%2C%22channel%22%3A{3}%7D&client=H5&clientVersion=9.2.0&uuid=&jsonp=jsonp_{1}_{2}'.format(
|
||
v_name, timestamp, v_num1, channel, venderId, activityId, qq_num, shopId)
|
||
try:
|
||
respon = requests.get(url=url, verify=False, headers=headers, timeout=60)
|
||
result = respon.text
|
||
return result
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
|
||
# 获取开卡结果
|
||
def getResult(resulttxt, userName, user_num):
|
||
r = re.compile(r'jsonp_.*?\((.*?)\)\;', re.M | re.S | re.I)
|
||
result = r.findall(resulttxt)
|
||
for i in result:
|
||
result_data = json.loads(i)
|
||
busiCode = result_data['busiCode']
|
||
if busiCode == '0':
|
||
message = result_data['message']
|
||
try:
|
||
result = result_data['result']['giftInfo']['giftList']
|
||
print(f"\t\t╰用户{user_num}【{userName}】:{message}")
|
||
for i in result:
|
||
print("\t\t\t╰{0}:{1} ".format(i['prizeTypeName'], i['discount']))
|
||
except:
|
||
print(f'\t\t╰用户{user_num}【{userName}】:{message}')
|
||
return busiCode
|
||
else:
|
||
print("\t\t╰用户{0}【{1}】:{2}".format(user_num, userName, result_data['message']))
|
||
return busiCode
|
||
|
||
|
||
def getRemoteShopid():
|
||
url = base64.decodebytes(
|
||
b"aHR0cHM6Ly9naXRlZS5jb20vY3VydGlubHYvUHVibGljL3Jhdy9tYXN0ZXIvT3BlbkNyYWQvc2hvcGlkLnR4dA==")
|
||
try:
|
||
rShopid= gettext(url)
|
||
rShopid=rShopid.split("\n")
|
||
return rShopid
|
||
except:
|
||
print("无法从远程获取shopid")
|
||
exitCodeFun(999)
|
||
|
||
# 读取shopid.txt
|
||
def getShopID():
|
||
|
||
shopid_path = os.path.join(os.path.split(sys.argv[0])[0], "shopid.txt")
|
||
try:
|
||
with open(shopid_path, "r", encoding="utf-8") as f:
|
||
shopid = f.read()
|
||
if len(shopid) > 0:
|
||
shopid = shopid.split("\n")
|
||
return shopid
|
||
else:
|
||
print("Error:请检查shopid.txt文件是否正常!\n")
|
||
exitCodeFun(2)
|
||
except Exception as e:
|
||
print("Error:请检查shopid.txt文件是否正常!\n", e)
|
||
exitCodeFun(2)
|
||
|
||
# 进度条
|
||
def progress_bar(start, end, threadNum):
|
||
print("\r", end="")
|
||
if threadNum == 2:
|
||
start2 = start - midNum
|
||
end2 = end - midNum
|
||
print("\n###[{1}]:线程{2}【当前进度: {0}%】\n".format(round(start2 / end2 * 100, 2), nowtime(), threadNum))
|
||
elif threadNum == 1:
|
||
print("\n###[{1}]:线程{2}【当前进度: {0}%】\n".format(round(start / end * 100, 2), nowtime(), threadNum))
|
||
sys.stdout.flush()
|
||
|
||
|
||
# 为多线程准备
|
||
def OpenVipCrad(startNum: int, endNum: int, shopids, cookies, userNames, pinNameList, threadNum):
|
||
for i in range(startNum, endNum):
|
||
user_num = 1
|
||
activityIdLabel = 0
|
||
for ck, userName, pinName in zip(cookies, userNames, pinNameList):
|
||
if i % 10 == 0 and i != 0:
|
||
progress_bar(i, endNum, threadNum)
|
||
try:
|
||
if len(shopids[i]) > 0:
|
||
headers_b = setHeaders(ck, "mall") # 获取请求头
|
||
venderId = getVenderId(shopids[i], headers_b) # 获取venderId
|
||
time.sleep(sleepNum) # 根据用户需求是否限制请求速度
|
||
# 新增记忆功能
|
||
memoryFun(i, threadNum, True, pinName, 0, allUserCount)
|
||
if activityIdLabel == 0:
|
||
headers_a = setHeaders(ck, "mh5")
|
||
activityId, getBean = getShopOpenCardInfo(venderId, headers_a, shopids[i], userName) # 获取入会礼包结果
|
||
# activityId,getBean 或 返回 0:没豆 1:有豆已是会员 2:记录模式(不入会)
|
||
time.sleep(sleepNum) # 根据用户需求是否限制请求速度
|
||
if activityId == 0 or activityId == 2:
|
||
break
|
||
elif activityId == 1:
|
||
user_num += 1
|
||
continue
|
||
elif activityId > 10:
|
||
activityIdLabel = 1
|
||
headers = setHeaders(ck, "JDApp")
|
||
result = bindWithVender(venderId, shopids[i], activityId, 208, headers)
|
||
busiCode = getResult(result, userName, user_num)
|
||
if busiCode == '0':
|
||
memoryFun(i, threadNum, False, pinName, getBean, allUserCount)
|
||
memoryJson = getMemory()
|
||
print(f"用户{user_num}:【{userName}】累计获得:{memoryJson['{}'.format(pinName)]} 京豆")
|
||
time.sleep(sleepNum)
|
||
else:
|
||
break
|
||
except Exception as e:
|
||
user_num += 1
|
||
print(e)
|
||
continue
|
||
user_num += 1
|
||
time.sleep(1)
|
||
progress_bar(endNum, endNum, threadNum)
|
||
|
||
# start
|
||
def start():
|
||
print(scriptHeader)
|
||
outfile("Readme.md", readmes, True)
|
||
isUpdate()
|
||
global endShopidNum, midNum, allUserCount
|
||
if isRemoteSid:
|
||
print("已启用远程获取shopid")
|
||
allShopid = getRemoteShopid()
|
||
else:
|
||
print("从本地shopid.txt获取shopid")
|
||
allShopid = getShopID()
|
||
allShopid = list(set(allShopid))
|
||
endShopidNum = len(allShopid)
|
||
midNum = int(endShopidNum / 2)
|
||
print("获取到店铺数量:", endShopidNum)
|
||
print(f"您已设置入会条件:{openCardBean} 京豆")
|
||
print("获取用户...")
|
||
cookies, userNames, pinNameList = iscookie()
|
||
allUserCount = len(cookies)
|
||
print("共{}个有效账号".format(allUserCount))
|
||
memorylabel = 0
|
||
startNum1 = 0
|
||
startNum2 = midNum
|
||
starttime = time.perf_counter() # 记录时间开始
|
||
if endShopidNum > 1:
|
||
# 如果启用记忆功能,则获取上一次记忆位置
|
||
startNum1, startNum2, memorylabel = isMemory(memorylabel, startNum1, startNum2, midNum, endShopidNum,
|
||
pinNameList)
|
||
# 多线程部分
|
||
threads = []
|
||
t1 = Thread(target=OpenVipCrad, args=(startNum1, startNum2, allShopid, cookies, userNames, pinNameList, 1))
|
||
threads.append(t1)
|
||
t2 = Thread(target=OpenVipCrad, args=(startNum2, endShopidNum, allShopid, cookies, userNames, pinNameList, 2))
|
||
threads.append(t2)
|
||
try:
|
||
for t in threads:
|
||
t.setDaemon(True)
|
||
t.start()
|
||
for t in threads:
|
||
t.join()
|
||
isSuccess = True
|
||
except:
|
||
isSuccess = False
|
||
elif endShopidNum == 1:
|
||
startNum1, startNum2, memorylabel = isMemory(memorylabel, startNum1, startNum2, midNum, endShopidNum,
|
||
pinNameList)
|
||
OpenVipCrad(startNum1, endShopidNum, allShopid, cookies, userNames, 1)
|
||
isSuccess = True
|
||
else:
|
||
print("获取到shopid数量为0")
|
||
exitCodeFun(9)
|
||
endtime = time.perf_counter() # 记录时间结束
|
||
if os.path.exists(pwd + "/log/memory.json"):
|
||
memoryJson = getMemory()
|
||
n = 1
|
||
for name,pinname in zip(userNames,pinNameList):
|
||
try:
|
||
userCountBean = memoryJson['pinname']
|
||
print(f"用户{n}:【{name}】:本次累计获得:{userCountBean}豆")
|
||
except:
|
||
print(f"用户{n}:【{name}】:本次累计获得:0 豆")
|
||
n += 1
|
||
|
||
time.sleep(1)
|
||
print("--- 入会总耗时 : %.03f 秒 seconds ---" % (endtime - starttime))
|
||
print("{0}\n{1}\n{2}".format("*" * 60, scriptHeader, remarks))
|
||
exitCodeFun(0)
|
||
|
||
if __name__ == '__main__':
|
||
start()
|